响应式画布动画,无需调整大小事件
窗口调整大小事件可以响应用户输入设备的移动而触发。当你调整画布大小时,它会自动清除,你将被迫重新呈现内容。对于动画,你可以通过 requestAnimationFrame
调用的主循环函数每帧执行此操作,这样可以最大限度地保持渲染与显示硬件同步。
调整大小事件的问题在于,当使用鼠标调整窗口大小时,事件的触发速度可能比浏览器的标准 60fps 速率快许多倍。当调整大小事件退出画布时,缓冲区被呈现给与显示设备不同步的 DOM,这可能导致剪切和其他负面影响。还有很多不必要的内存分配和释放可能会在 GC 清理一段时间后进一步影响动画。
Debounced resize 事件
处理调整大小事件的高射击率的常用方法是去除调整大小事件。
// Assume canvas is in scope
addEventListener.("resize", debouncedResize );
// debounce timeout handle
var debounceTimeoutHandle;
// The debounce time in ms (1/1000th second)
const DEBOUNCE_TIME = 100;
// Resize function
function debouncedResize () {
clearTimeout(debounceTimeoutHandle); // Clears any pending debounce events
// Schedule a canvas resize
debounceTimeoutHandle = setTimeout(resizeCanvas, DEBOUNCE_TIME);
}
// canvas resize function
function resizeCanvas () { ... resize and redraw ... }
上面的示例将画布的大小调整延迟到调整大小事件后的 100ms。如果在此时触发了进一步的调整大小事件,则取消现有的调整大小超时并调度新的调整大小。这有效地消耗了大多数调整大小事件。
它仍然存在一些问题,最值得注意的是调整大小和查看调整大小的画布之间的延迟。减少去抖时间可以改善这一点,但调整大小仍然与显示设备不同步。你还将动画主循环渲染到不合适的画布。
更多代码可以减少问题! 更多代码也会产生自己的新问题。
简单而且最好调整大小
尝试了许多不同的方法来平滑画布的大小调整,从荒谬的复杂到忽略问题(谁还在乎呢?)我又回到了一个可靠的朋友身上。
K.I.S.S 是大多数程序员应该知道的(Keep It Simple Stupid),而且事实证明最好的解决方案是最简单的的。
只需在主动画循环中调整画布大小即可。它与显示设备保持同步,没有不必要的渲染,并且在保持全帧速率的同时资源管理是最小的。你也不需要向窗口或任何其他调整大小函数添加 resize 事件。
通过检查画布大小是否与窗口大小匹配,可以在通常清除画布的位置添加调整大小。如果没有调整大小。
// Assumes canvas element is in scope as canvas
// Standard main loop function callback from requestAnimationFrame
function mainLoop(time) {
// Check if the canvas size matches the window size
if (canvas.width !== innerWidth || canvas.height !== innerHeight) {
canvas.width = innerWidth; // resize canvas
canvas.height = innerHeight; // also clears the canvas
} else {
ctx.clearRect(0, 0, canvas.width, canvas.height); // clear if not resized
}
// Animation code as normal.
requestAnimationFrame(mainLoop);
}