requestAnimationFrame
回撥函式在瀏覽器事件迴圈的 渲染階段 (rendering phase) 執行,更具體地說,是在繪製之前 (before paint)。
以下是事件迴圈各階段的簡要說明以及 requestAnimationFrame
的位置:
- 輸入事件 (Input events): 處理使用者互動,例如點選、滑鼠移動、鍵盤輸入等。
- 微任務 (Microtasks): 執行 Promise 的
then()
、catch()
、finally()
,以及queueMicrotask()
等。微任務佇列的優先順序高於宏任務佇列。 - 更新渲染 (Update the rendering): 這是
requestAnimationFrame
回撥執行的地方。瀏覽器會計算佈局 (layout) 和繪製 (paint) 所需的資訊。- requestAnimationFrame: 在這一階段的早期執行,允許你在瀏覽器繪製下一幀之前更新動畫或執行其他視覺相關的操作。這確保了動畫的流暢性,並避免了丟幀。
- 佈局 (Layout): 計算元素的位置和大小。
- 繪製 (Paint): 將畫素繪製到螢幕上。
- 合成 (Composite): 如果頁面包含多個圖層,瀏覽器會將它們合成為最終的影像。
- 空閒回撥 (Idle callbacks): 執行
requestIdleCallback
回撥,用於執行低優先順序的任務,例如載入非關鍵資源。 - 宏任務 (Macrotasks): 執行
setTimeout
、setInterval
、setImmediate
(Node.js)、I/O 操作等。
為什麼在渲染階段執行很重要?
在渲染階段執行 requestAnimationFrame
回撥有幾個關鍵優勢:
- 同步性:
requestAnimationFrame
回撥與瀏覽器的渲染週期同步,這意味著你的程式碼會在瀏覽器準備繪製下一幀之前執行。這有助於避免丟幀和動畫卡頓。 - 效率: 瀏覽器可以最佳化
requestAnimationFrame
回撥的執行,例如批次處理多個回撥,以提高效能。 - 節能: 當頁面不在活動選項卡時,瀏覽器會暫停
requestAnimationFrame
回撥的執行,從而節省電量。
示例:
function animate() {
// 更新動畫狀態
requestAnimationFrame(animate); // 請求下一幀動畫
}
animate(); // 啟動動畫迴圈
在這個例子中,animate
函式會在每一幀被呼叫,從而建立流暢的動畫效果。由於 requestAnimationFrame
在渲染階段執行,瀏覽器可以確保動畫與螢幕重新整理頻率同步,從而提供最佳的使用者體驗。