2013-12-22 3 views
1

Я пытаюсь сделать анимацию «растущего дерева». Проблема в том, что я не могу подключить эти две функции ниже, чтобы заставить ее работать правильно.Подключение рекурсии вместе с другой функцией | canvas, JavaScript

До сих пор у меня есть функция, которая рисует прямую линию (магистральный) от основания до вершины:
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); 

Я пробовал несколько вариантов, но ни один из них не дал правильный результат, поэтому я решил попросить о помощи.

+0

** Предупреждение **: Ваша скрипка использует 'setInterval', но никогда не останавливает ее! Обязательно используйте 'clearInterval' или лучше используйте' setTimeout' с условными обозначениями. – Oriol

ответ

2

Проблема с анимацией заключается в том, что вам необходимо разделить алгоритм пошагово.

Это означает, что вы должны преобразовать свой рекурсивный алгоритм в итеративный.

Чтобы сделать это, вы можете:

  1. Разделите stick функцию в подфункции [Demo]:

    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()); 
        } 
    } 
    

Полный код [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()); 
    } 
} 

Теперь, это тривиально, чтобы преобразовать его в анимации. Просто замените время цикла следующим:

(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(); 
})(); 
+0

Спасибо большое! Это действительно полезно. Я буду играть с кодом. – user3127305

Смежные вопросы