響應式畫布動畫,無需調整大小事件

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