cancelAnimationFrame
单独使用不需要停止动画循环(事实证明)。
绝对确定您还需要使用条件检查- 一个通用示例:
var isPlaying; /// our condition
function loop() {
/* funky stuff here */
If (isPlaying === true)
requestId = requestAnimationFrame(loop);
}
然后启动它:
functiom start() {
isPlaying = true;
loop();
}
现在,当您想要停止动画时,您需要执行以下操作:
function stop() {
isPlaying = false;
/// kill any request in progress
if (requestId) cancelAnimationFrame(requestId);
}
如果你不这样做,你就有可能将调用堆叠到循环中——例如:
如果您调整大小并且 cAF 没有阻止 rAF 重新触发循环,则旧循环仍将在后台运行,您将在此基础上启动一个新循环。
这就是为什么您会看到速度增加的原因,因为旧循环和新循环都会在星星被绘制到屏幕之前增加位置。
在第三次重新调整大小时,又开始了另一个循环,最终整个事情将阻塞浏览器。
然而
我建议您使用以下方法,而不是利用循环的开始和停止:
- 一次创建画布
- 只启动一次循环
- 在这种情况下,整个调整大小机制的重构可能是有益的(例如,将所需的初始化(元素的宽度和高度)与以后可以重用的第一次初始化分开)。
- 无需为每个重新调整大小重新初始化星星,因为您将使用宽度和高度来检查它们的边界(画布将进行剪裁)。
调整大小时,您可以考虑使用条件标志来防止在调整大小时渲染。
尽管通常情况下,由于 JavaScript 的单线程特性,实际上不需要在画布更改大小时阻止渲染的条件,在这种情况下,您需要对当前元素大小进行边界检查。Canvas 本身会为您处理剪裁。
话虽如此:不需要每次都重新创建画布元素。
这会产生不必要的开销。如果画布已经创建,则简单地在其属性上设置新的宽度和高度:
if (typeof canvas === 'undefined')
canvas = /* only create if it doesn't exist */
canvas.width = width;
canvas.height = height;
PS:我用上面的一些蛮力实现“阻碍”了一个版本。它远非完整或高质量,但为了举例说明给你一些建议,它消除了一些痛苦。
请采用适合您的需要。
更新:
要包含来自附加评论的更多信息:
当在画布元素上设置新大小时,其上下文将重置为默认值(fillStyle
变为透明、strokeStyle
黑色、重置变换等)。
这意味着在设置每个新尺寸后,必须重新设置所有非默认设置。
设置新的大小可能(并且通常会)清除画布的内容,以便所有像素变为透明黑色。