如何在分形绘图递归函数中创建延迟
How to create delay in fractal drawing recursive function
我正在玩 Eloquent JavaScript 中遇到的分形绘图递归函数。
我想为每个分支的绘制设置延迟 - 目的是在修改此函数及其参数时可视化 branches/recursive 调用的流程。
我在下面的代码中使用 setTimeout
的方式似乎不起作用,我很困惑为什么。
我预计 cx.fillRect(...)
每次延迟都会画一个分支;而不是堆叠在队列中,因为 setTimeout
之外没有其他代码等待。
下面我首先包含了工作分形绘图 html/js 代码,没有尝试包含延迟。第二段代码是我尝试包含 setTimeout
延迟。
我尝试使用 setTimeout
的无效尝试:
<canvas width="600" height="300"></canvas>
<script>
let cx = document.querySelector("canvas").getContext("2d");
function branch(length, angle, scale) {
setTimeout(() => {
cx.fillRect(0, 0, 1, length);
if (length < 8) return;
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();
}, 80);
}
cx.translate(300, 0);
branch(60, 0.5, 0.8);
</script>
没有延迟的工作代码:
<canvas width="600" height="300"></canvas>
<script>
let cx = document.querySelector("canvas").getContext("2d");
function branch(length, angle, scale) {
cx.fillRect(0, 0, 1, length);
if (length < 8) return;
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();
}
cx.translate(300, 0);
branch(60, 0.5, 0.8);
</script>
setTimeout
的尝试延迟了第一次调用,然后对其具有相同超时的子树产生两次递归调用,导致它们相互走过,依此类推。
所有递归调用都需要等待整个左子树完成绘制,然后再继续右子树,右子树也需要完成,然后调用才能解析并让父级继续进行下一个操作(右子树或解决)。您不能同时让两个不同的调用帧弄乱同一个 canvas 堆栈。
我会使用 promises 来做到这一点;这使您可以管理 setTimeout
的顺序并使用 sleep
函数设置所需的延迟,基本上是一个 promisified setTimeout
.
const sleep = ms => new Promise(
resolve => setTimeout(resolve, ms)
);
const cx = document.querySelector("canvas").getContext("2d");
async function branch(length, angle, scale) {
cx.fillRect(0, 0, 1, length);
if (length < 8) {
return;
}
await sleep(50); // delay in ms, good to make into a parameter
cx.save();
cx.translate(0, length);
cx.rotate(-angle);
await branch(length * scale, angle, scale);
cx.rotate(2 * angle);
await branch(length * scale, angle, scale);
cx.restore();
}
cx.translate(300, 0);
branch(60, 0.5, 0.8);
<canvas width="600" height="300"></canvas>
为了比较,下面是如何在没有 promise 的情况下使用回调来做到这一点,每个子级都会触发回调以向其父级发出信号,告知其已完成,以便父级知道何时绘制下一个子树或解析:
const cx = document.querySelector("canvas").getContext("2d");
function branch(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);
<canvas width="600" height="300"></canvas>
由于您要在 canvas 上制作动画,您可以考虑使用 requestAnimationFrame
循环绘制每一帧的生成器函数。英国皇家空军提供 .
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;
(function drawFrame(time) {
!done && requestAnimationFrame(drawFrame);
if (time && lastTime === 0) {
lastTime = time;
}
else if (lastTime > 0 && time >= lastTime + speedMs) {
({done} = branchGen.next());
lastTime += speedMs;
}
})();
<canvas width="600" height="300"></canvas>
我正在玩 Eloquent JavaScript 中遇到的分形绘图递归函数。
我想为每个分支的绘制设置延迟 - 目的是在修改此函数及其参数时可视化 branches/recursive 调用的流程。
我在下面的代码中使用 setTimeout
的方式似乎不起作用,我很困惑为什么。
我预计 cx.fillRect(...)
每次延迟都会画一个分支;而不是堆叠在队列中,因为 setTimeout
之外没有其他代码等待。
下面我首先包含了工作分形绘图 html/js 代码,没有尝试包含延迟。第二段代码是我尝试包含 setTimeout
延迟。
我尝试使用 setTimeout
的无效尝试:
<canvas width="600" height="300"></canvas>
<script>
let cx = document.querySelector("canvas").getContext("2d");
function branch(length, angle, scale) {
setTimeout(() => {
cx.fillRect(0, 0, 1, length);
if (length < 8) return;
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();
}, 80);
}
cx.translate(300, 0);
branch(60, 0.5, 0.8);
</script>
没有延迟的工作代码:
<canvas width="600" height="300"></canvas>
<script>
let cx = document.querySelector("canvas").getContext("2d");
function branch(length, angle, scale) {
cx.fillRect(0, 0, 1, length);
if (length < 8) return;
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();
}
cx.translate(300, 0);
branch(60, 0.5, 0.8);
</script>
setTimeout
的尝试延迟了第一次调用,然后对其具有相同超时的子树产生两次递归调用,导致它们相互走过,依此类推。
所有递归调用都需要等待整个左子树完成绘制,然后再继续右子树,右子树也需要完成,然后调用才能解析并让父级继续进行下一个操作(右子树或解决)。您不能同时让两个不同的调用帧弄乱同一个 canvas 堆栈。
我会使用 promises 来做到这一点;这使您可以管理 setTimeout
的顺序并使用 sleep
函数设置所需的延迟,基本上是一个 promisified setTimeout
.
const sleep = ms => new Promise(
resolve => setTimeout(resolve, ms)
);
const cx = document.querySelector("canvas").getContext("2d");
async function branch(length, angle, scale) {
cx.fillRect(0, 0, 1, length);
if (length < 8) {
return;
}
await sleep(50); // delay in ms, good to make into a parameter
cx.save();
cx.translate(0, length);
cx.rotate(-angle);
await branch(length * scale, angle, scale);
cx.rotate(2 * angle);
await branch(length * scale, angle, scale);
cx.restore();
}
cx.translate(300, 0);
branch(60, 0.5, 0.8);
<canvas width="600" height="300"></canvas>
为了比较,下面是如何在没有 promise 的情况下使用回调来做到这一点,每个子级都会触发回调以向其父级发出信号,告知其已完成,以便父级知道何时绘制下一个子树或解析:
const cx = document.querySelector("canvas").getContext("2d");
function branch(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);
<canvas width="600" height="300"></canvas>
由于您要在 canvas 上制作动画,您可以考虑使用 requestAnimationFrame
循环绘制每一帧的生成器函数。英国皇家空军提供
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;
(function drawFrame(time) {
!done && requestAnimationFrame(drawFrame);
if (time && lastTime === 0) {
lastTime = time;
}
else if (lastTime > 0 && time >= lastTime + speedMs) {
({done} = branchGen.next());
lastTime += speedMs;
}
})();
<canvas width="600" height="300"></canvas>