将递归与其他函数连接在一起|画布,JavaScript

时间:2013-12-22 15:37:49

标签: javascript animation canvas recursion

我正在尝试制作“成长树”的动画。问题是我无法连接下面这两个函数以使其正常工作。

到目前为止,我有一个从底部到顶部绘制直线(树干)的功能:
http://jsfiddle.net/FTCcW/1/

这是绘制整个树的函数的代码:

function stick(d) {

    if (d==0)
        return;

    context.beginPath();
    context.moveTo(0,70);
    context.lineTo(0,0);
    context.lineWidth = 3;
    context.strokeStyle = 'gray';
    context.stroke();

    if (d==1) {
        context.strokeStyle = 'green';
        context.stroke(); }


    context.save();
    context.scale(0.75,0.75);
    context.translate(-35,-60);
    context.rotate(-30 * Math.PI/180);
    stick(d-1);
    context.restore();


    context.save();
    context.scale(0.75,0.75);
    context.translate(35,-60);
    context.rotate(30 * Math.PI/180);
    stick(d-1);
    context.restore();
}

stick(17);

我尝试了几个选项,但没有一个能给出正确的结果,所以我决定寻求帮助。

1 个答案:

答案 0 :(得分:2)

动画的问题在于你必须分步算法。

这意味着您必须将递归算法转换为迭代算法。

为此,你可以:

  1. 在子功能[Demo]中拆分stick功能:

    function pre(i) {
        context.save();
        context.scale(0.75,0.75);
        context.translate(i * 35,-60);
        context.rotate(i * 30 * Math.PI/180); 
    }
    function post() {
        context.restore();
    }
    function middle(d) {
        context.beginPath();
        context.moveTo(0,70);
        context.lineTo(0,0);
        context.lineWidth = 3;
        context.strokeStyle = 'gray';
        context.stroke();
    
        if (d==1) {
            context.strokeStyle = 'green';
            context.stroke();
        }
    }
    function stick(d, i) {
        if(i) pre(i);
        if(d > 0) {
            middle(d);
            stick(d-1, -1);
            stick(d-1, 1);
        }
        if(i) post();
    }
    
  2. 不是调用函数,而是将调用推送到队列(数组),然后循环它:

    function stick(n, i) {
        function main(d, i) {
            // Note the order of pushing is the inverse!
            // You must push first the last function
    
            if(i) queue.push([], post);
            if(d > 0) {
                queue.push([d-1,-1], main);
                queue.push([d-1,1], main);
                queue.push([d], middle);
            }
            if(i) queue.push([i], pre);
        }
    
        queue.push([n, 0], main);
    
        while(queue.length) {
            (queue.pop()).apply(null, queue.pop());
        }
    }
    
  3. 完整代码[Demo]:

    function stick(n, i) {
        var queue = [];
    
        function pre(i) {
            context.save();
            context.scale(0.75,0.75);
            context.translate(i * 35,-60);
            context.rotate(i * 30 * Math.PI/180); 
        }
        function post() {
            context.restore();
        }
        function middle(d) {
            context.beginPath();
            context.moveTo(0,70);
            context.lineTo(0,0);
            context.lineWidth = 3;
            context.strokeStyle = 'gray';
            context.stroke();
    
            if (d==1) {
                context.strokeStyle = 'green';
                context.stroke();
            }
        }
        function main(d, i) {
            if(i) queue.push([], post);
            if(d > 0) {
                queue.push([d-1,-1], main);
                queue.push([d-1,1], main);
                queue.push([d], middle);
            }
            if(i) queue.push([i], pre);
        }
    
        queue.push([n, 0], main);
    
        while(queue.length) {
            (queue.pop()).apply(null, queue.pop());
        }
    }
    

    现在,将它转换为动画是微不足道的。只需用以下内容替换while循环:

    (function step() {
        if (queue.length) {
            (queue.pop()).apply(null, queue.pop());
            setTimeout(step, 100);
        }
    })();
    

    但是,由于只有main函数可以进行视觉更改,因此最好使用[Demo]

    (function step() {
        if (queue.length) {
            var f = queue.pop(),
                args = queue.pop();
            f.apply(null, args);
            if(f === main) setTimeout(step, 100);
            else step();
        }
    })();
    

    或者您可能希望在每个步骤执行更多操作,[Demo]:

    var iter = 1000;
    (function step() {
        var i = iter,
            d = new Date();
        while (queue.length && --i>=0) {
            var f = queue.pop(),
                args = queue.pop();
            f.apply(null, args);
        }
        iter = Math.max(50, iter*60/(new Date()-d)|0);
        if (queue.length) f === main ? setTimeout(step, 100) : step();
    })();