響應式畫布動畫,無需調整大小事件
視窗調整大小事件可以響應使用者輸入裝置的移動而觸發。當你調整畫布大小時,它會自動清除,你將被迫重新呈現內容。對於動畫,你可以通過 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);
}