如何使用setTimeout延迟绘制递归函数

时间:2018-01-31 15:32:17

标签: html recursion canvas settimeout

我找到了一个很好的递归函数示例,它生成一个简单的分形树here(如下所示)。



    var canvas = document.getElementById('canvas_main');
    canvas.width  = 600;
    canvas.height = 600;
    var ctx = canvas.getContext('2d');

    function draw(x, y, len, ang){
        ctx.save();

        ctx.beginPath();
        ctx.translate(x, y);
        ctx.rotate(ang * Math.PI/180);
        ctx.moveTo(0, 0);
        ctx.lineTo(0, -len);
        ctx.stroke();

        if (len < 10) {
            ctx.restore();
            return;
        }

        draw(0, -len, len*0.8, -15);
        draw(0, -len, len*0.8,  15);
        ctx.restore();
    }
    draw(300, 600, 120, 0);
&#13;
<canvas id="canvas_main"></canvas>
&#13;
&#13;
&#13;

我想延迟迭代的绘制。我认为setTimeout()函数可以帮助我,但我似乎无法做到正确。

最让我困惑的是为什么简单地包装draw()函数不会起作用:

setTimeout(function(){
    draw(0, -len, len*0.8,  15);
    draw(0, -len, len*0.8, -15);
    ctx.restore();
},500)

上下文转换给我带来了麻烦。如果第二个绘图功能和恢复功能被禁用,我可以让一方工作:

setTimeout(function(){
    draw(0, -len, len*0.8,  15);
    //draw(0, -len, len*0.8, -15);
    //ctx.restore();
},500)

但我没有比这更进一步。如何逐步绘制整棵树? 有人可以解释为什么包装不起作用?谢谢!

编辑: 作为一个额外的问题,是否可以使用requestAnimationFrame()方法执行此操作?

1 个答案:

答案 0 :(得分:1)

超时不会累积。

用于每次迭代移动线的方法不会跟踪分支的位置,而是依赖于ctx.save()ctx.restore()状态堆栈函数来返回正确的位置以移动到下一个分支。

使用set timeout意味着在递归到达最后一个分支之前调用恢复。

setTimeout(function(){
    draw(0, -len, len*0.8,  15); // this function draws and exits 
    draw(0, -len, len*0.8, -15); // this function draws and exits 
    ctx.restore(); // the restore is called before the above two functions
                   // have completed their next iterations as that is waiting
                   // for the timeouts.
},500)

只有在绘制了上面的所有分支后才能调用恢复。

保持状态在函数中。

简单的方法是删除保存和恢复并手动执行转换。这样,每个分支的状态都存储在函数中,而不是单独的堆栈。

以下是使用超时的修改。 x和y位置现在包含分支开始位置,下一个分支位置在代码中计算。

超时对每个分支都有一个,并且时间稍微随机化,这使渲染流程保持平滑。

还添加了线宽以获得乐趣。 :)

&#13;
&#13;
var canvas = document.getElementById('canvas_main');
    canvas.width  = 600;
    canvas.height = 600;
    var ctx = canvas.getContext('2d');

    function draw(x, y, len, ang, width){

        ctx.lineWidth = width;

        // draw the branch
        ctx.beginPath();
        ctx.lineTo(x, y);

        // get the end position
        x += Math.cos(ang) * len;
        y += Math.sin(ang) * len;

        ctx.lineTo(x, y);
        ctx.stroke();

        if (len > 10) {

          setTimeout(()=>{                
            draw(x , y , len * 0.8, ang - 0.2, width * 0.8);
          }, 300 + Math.random() * 100);
          setTimeout(()=>{
            draw(x , y , len * 0.8, ang + 0.2, width * 0.8);
          }, 300 + Math.random() * 100);
        }
    }
    draw(300, 600, 120, -Math.PI /2,4);
&#13;
<canvas id="canvas_main"></canvas>
&#13;
&#13;
&#13;