Reputation: 135
I found a nice example of a recursive function that generates a simple fractal tree here (shown below).
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);
<canvas id="canvas_main"></canvas>
I would like to delay the drawing of the iterations. I thought the setTimeout() function could help me out, but I can't seem to get it right.
What confuses me most is why simply wrapping the draw() functions won't work:
setTimeout(function(){
draw(0, -len, len*0.8, 15);
draw(0, -len, len*0.8, -15);
ctx.restore();
},500)
The context transformation gives me trouble. I can get one side to work if the second draw function and the restore function is disabled like this:
setTimeout(function(){
draw(0, -len, len*0.8, 15);
//draw(0, -len, len*0.8, -15);
//ctx.restore();
},500)
But I don't get any furter than this. How can I draw the entire tree in steps? And can someone explain why exactly the wrapping isn't working? Thanks!
Edit: As a bonus question, is it possible to do this with the requestAnimationFrame() method?
Upvotes: 1
Views: 185
Reputation: 54026
The method used to move the line for each iteration does not track the position of the branch, but relies on the ctx.save()
and ctx.restore()
state stack functions to return the correct position to move to the next branch.
Using set timeout means that the restore is called before recursion has reached the last branch.
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)
The restore can only be called after all the branches above have been drawn.
The easy way is to remove the save and restores and do the transformation manually. That way the state of each branch is stored in the function, no a separate stack.
Below is a modification using timeouts. The x, and y position now contain the branch start position and the next branch position is calculated in code.
The timeouts have one for each branch and the times are randomized a little, this keeps the flow of renders smooth.
Also added a line width for the fun of it. :)
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);
<canvas id="canvas_main"></canvas>
Upvotes: 2