Skip to content Skip to sidebar Skip to footer

How To Create Delay In Fractal Drawing Recursive Function

I am playing around with a fractal drawing recursive function encountered in Eloquent JavaScript. I want to set a delay for the drawing of each branch - for the purpose of visualiz

Solution 1:

The attempt with setTimeout delays the first call, then spawns two recursive calls for its subtrees that have the same timeout, leading to them walking over each other, and so on down.

All recursive calls need to wait for the entire left subtree to complete drawing before moving on to the right one, which also needs to complete before the call can resolve and let the parent proceed to its next operation (right subtree or resolving). You can't have two different call frames messing with the same canvas stack at the same time.

I'd use promises to do this; this lets you manage the ordering of setTimeouts and set the desired delay with a sleep function, basically a promisified setTimeout.

constsleep = ms => newPromise(
  resolve =>setTimeout(resolve, ms)
);

const cx = document.querySelector("canvas").getContext("2d");

asyncfunctionbranch(length, angle, scale) {
  cx.fillRect(0, 0, 1, length);

  if (length < 8) {
    return;
  }

  awaitsleep(50); // delay in ms, good to make into a parameter
  cx.save();
  cx.translate(0, length);
  cx.rotate(-angle);
  awaitbranch(length * scale, angle, scale);
  cx.rotate(2 * angle);
  awaitbranch(length * scale, angle, scale);
  cx.restore();
}

cx.translate(300, 0);
branch(60, 0.5, 0.8);
<canvaswidth="600"height="300"></canvas>

For comparison, here's how you could do it without promises using a callback that each child fires to signal to its parent that it's done, so the parent knows when to draw the next subtree or resolve:

const cx = document.querySelector("canvas").getContext("2d");

functionbranch(length, angle, scale, done) {
  cx.fillRect(0, 0, 1, length);

  if (length < 8) {
    done && done();
    return;
  }

  setTimeout(() => {
    cx.save();
    cx.translate(0, length);
    cx.rotate(-angle);
    branch(length * scale, angle, scale, () => {
      cx.rotate(2 * angle);
      branch(length * scale, angle, scale, () => {
        cx.restore();
        done && done();
      });
    });    
  }, 50);
}

cx.translate(300, 0);
branch(60, 0.5, 0.8);
<canvaswidth="600"height="300"></canvas>

Since you're doing an animation on canvas, you might consider using requestAnimationFrame to loop through a generator function that draws each frame. RAF offers better-quality animations than setTimeout.

const cx = document.querySelector("canvas").getContext("2d");

function *branch(length, angle, scale) {
  cx.fillRect(0, 0, 1, length);

  if (length < 8) {
    return;
  }

  yield;
  cx.save();
  cx.translate(0, length);
  cx.rotate(-angle);
  yield *branch(length * scale, angle, scale);
  cx.rotate(2 * angle);
  yield *branch(length * scale, angle, scale);
  cx.restore();
}

cx.translate(300, 0);
const branchGen = branch(60, 0.5, 0.8);
const speedMs = 50;
let lastTime = 0;
let done = false;

(functiondrawFrame(time) {
  !done && requestAnimationFrame(drawFrame);

  if (time && lastTime === 0) {
    lastTime = time;
  }
  elseif (lastTime > 0 && time >= lastTime + speedMs) {
    ({done} = branchGen.next());
    lastTime += speedMs;
  }
})();
<canvaswidth="600"height="300"></canvas>

Post a Comment for "How To Create Delay In Fractal Drawing Recursive Function"