學習API以可預測的方式執行動畫和安排事件
requestAnimationFrame()
是相對較新的瀏覽器API。它提供了一種更可預測的方式來連接瀏覽器渲染週期。
當前所有現代瀏覽器(和IE 10+)都支持它。
它不是特定於動畫的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,則這是理想的時間表:
您有一組繪畫(綠色)和渲染(紫色)事件,並且您的代碼在黃色框中-順便說一句,這些是瀏覽器開發工具中用於表示時間軸的顏色:
下圖顯示了理想的情況。您每60毫秒就有一次繪畫和渲染,而動畫則介於兩者之間,完全規則。
如果您為動畫功能使用了更高頻率的調用:
請注意,在進行任何渲染之前,在每個幀中我們如何調用4個動畫步驟,這將使動畫感覺非常不穩定。
如果setTimeout由於其他代碼阻塞事件循環而無法按時運行怎麼辦?我們最終錯過了幀:
如果動畫步驟所花費的時間超出您的預期,該怎麼辦?
渲染和繪畫事件也將被延遲。
這就是requestAnimationFrame()
直觀地工作:
所有動畫代碼都會運行前渲染和繪畫事件。這使得代碼更加可預測,並且有很多時間來製作動畫,而不必擔心會超過我們可支配的16毫秒時間。
查看傑克·阿奇博爾德(Jake Archibald)的精彩視頻關於這個話題。
免費下載我的JavaScript初學者手冊
更多瀏覽器教程:
- HTML5提供了一些有用的技巧
- 我如何使基於CMS的網站脫機工作
- 漸進式Web應用程序完整指南
- 提取API
- 推送API指南
- 頻道消息傳遞API
- 服務人員教程
- 緩存API指南
- 通知API指南
- 深入IndexedDB
- Selectors API:querySelector和querySelectorAll
- 通過延遲和異步有效地加載JavaScript
- 文檔對像模型(DOM)
- Web存儲API:本地存儲和會話存儲
- 了解HTTP Cookies的工作原理
- 歷史API
- WebP圖像格式
- XMLHttpRequest(XHR)
- 深入的SVG教程
- 什麼是數據網址
- 學習網絡平台的路線圖
- CORS,跨域資源共享
- 網絡工作者
- requestAnimationFrame()指南
- 什麼是Doctype
- 使用DevTools控制台和控制台API
- 語音合成API
- 如何在純JavaScript中等待DOM ready事件
- 如何將類添加到DOM元素
- 如何遍歷來自querySelectorAll的DOM元素
- 如何從DOM元素中刪除類
- 如何檢查DOM元素是否具有類
- 如何更改DOM節點值
- 如何將click事件添加到從querySelectorAll返回的DOM元素列表中
- WebRTC,實時Web API
- 如何在JavaScript中獲取元素的滾動位置
- 如何替換DOM元素
- 如何只接受輸入文件字段中的圖像
- 為什麼要使用瀏覽器的預覽版?
- 斑點對象
- 文件對象
- FileReader對象
- FileList對象
- ArrayBuffer
- ArrayBufferView
- URL對象
- 類型數組
- DataView對象
- BroadcastChannel API
- Streams API
- FormData對象
- 導航器對象
- 如何使用地理位置API
- 如何使用getUserMedia()
- 如何使用拖放API
- 如何在網頁上滾動
- 在JavaScript中處理表單
- 鍵盤事件
- 鼠標事件
- 觸摸事件
- 如何從DOM元素中刪除所有子級
- 如何使用原始Javascript創建HTML屬性
- 如何檢查是否使用JavaScript選中了複選框?
- 如何使用JavaScript複製到剪貼板
- 如何使用JavaScript禁用按鈕
- 如何在瀏覽器中使頁面可編輯
- 如何使用URLSearchParams在JavaScript中獲取查詢字符串值
- 如何一次刪除頁面上的所有CSS
- 如何使用insertAdjacentHTML
- Safari,退出前警告
- 如何使用JavaScript將圖像添加到DOM
- 如何重設表格
- 如何使用Google字體