requestAnimationFrame()指南

學習API以可預測的方式執行動畫和安排事件

requestAnimationFrame()是相對較新的瀏覽器API。它提供了一種更可預測的方式來連接瀏覽器渲染週期。

當前所有現代瀏覽器(和IE 10+)都支持它。

Browser support

它不是特定於動畫的API,但是使用最多的地方是動畫。

JavaScript有一個事件循環。它持續運行以執行JavaScript。

過去,動畫是使用setTimeout()或者setInterval()。您執行了一點動畫,然後調用setTimeout()從現在起幾毫秒內再次重複此代碼:

const performAnimation = () => {
  //...
  setTimeout(performAnimation, 1000 / 60)
}
setTimeout(performAnimation, 1000 / 60)

或者

const performAnimation = () => {
  //...
}
setInterval(performAnimation, 1000 / 60)

您可以通過獲取超時或間隔參考並清除動畫來停止動畫:

let timer

const performAnimation = () => { //… timer = setTimeout(performAnimation, 1000 / 60) }

timer = setTimeout(performAnimation, 1000 / 60)

//… clearTimeout(timer)

1000 / 60之間的間隔performAnimation()呼叫取決於監視器的刷新率,在大多數情況下,刷新率是60 Hz(每秒60次重繪),因為如果監視器由於其限製而無法顯示重繪,則執行重繪是無用的。這樣一來,我們就有大約16.6ms的時間來顯示每個幀。

這種方法的問題在於,即使我們精確地指定了此精度,瀏覽器仍可能忙於執行其他操作,而setTimeout調用可能無法及時進行重新繪製,因此它將延遲到下一個週期。

這很不好,因為我們丟了一幀,然後在下一幀動畫執行了2次,導致眼睛注意到笨拙的動畫。

在一個小故障上檢查此示例使用setTimeout()構建的動畫

requestAnimationFrame是執行動畫的標準方法,儘管代碼看起來與setTimeout / setInterval代碼非常相似,但事件的處理方式卻非常不同:

let request

const performAnimation = () => { request = requestAnimationFrame(performAnimation) //animate something }

requestAnimationFrame(performAnimation)

//… cancelAnimationFrame(request) //stop the animation

這個例子關於毛刺使用requestAnimationFrame()構建的動畫顯示如何。

優化

requestAnimationFrame()因為它的引入對CPU非常友好,如果當前窗口或選項卡不可見,則會導致動畫停止。

在引入requestAnimationFrame()時,即使隱藏了選項卡,setTimeout / setInterval的確運行了,但是現在由於這種方法在節省電池方面也很成功,因此瀏覽器還對這些事件實施了限制,每秒最多執行1次。

使用requestAnimationFrame瀏覽器可以進一步優化資源消耗並使動畫更流暢。

時間軸示例

如果使用setTimeout或setInterval,則這是理想的時間表:

Perfect timeline

您有一組繪畫(綠色)和渲染(紫色)事件,並且您的代碼在黃色框中-順便說一句,這些是瀏覽器開發工具中用於表示時間軸的顏色:

The devtools timeline

下圖顯示了理想的情況。您每60毫秒就有一次繪畫和渲染,而動畫則介於兩者之間,完全規則。

如果您為動畫功能使用了更高頻率的調用:

Too frequent timeline

請注意,在進行任何渲染之前,在每個幀中我們如何調用4個動畫步驟,這將使動畫感覺非常不穩定。

如果setTimeout由於其他代碼阻塞事件循環而無法按時運行怎麼辦?我們最終錯過了幀:

Missed frame

如果動畫步驟所花費的時間超出您的預期,該怎麼辦?

Delay in the timeline

渲染和繪畫事件也將被延遲。

這就是requestAnimationFrame()直觀地工作:

Delay in the timeline

所有動畫代碼都會運行渲染和繪畫事件。這使得代碼更加可預測,並且有很多時間來製作動畫,而不必擔心會超過我們可支配的16毫秒時間。

查看傑克·阿奇博爾德(Jake Archibald)的精彩視頻關於這個話題。

免費下載我的JavaScript初學者手冊


更多瀏覽器教程: