requestAnimationFrame用法優(yōu)化源碼解析
介紹
什么是requestAnimationFrame
requestAnimationFrame
是瀏覽器用于定時循環(huán)操作的一個API,通常用于動畫和游戲開發(fā)。它會把每一幀中的所有DOM操作集中起來,在重繪之前一次性更新,并且關(guān)聯(lián)到瀏覽器的重繪操作。
為什么使用requestAnimationFrame而不是setTimeout或setInterval
與setTimeout
或setInterval
相比,requestAnimationFrame
具有以下優(yōu)勢:
- 通過系統(tǒng)時間間隔來調(diào)用回調(diào)函數(shù),無需擔心系統(tǒng)負載和阻塞問題,系統(tǒng)會自動調(diào)整回調(diào)頻率。
- 由瀏覽器內(nèi)部進行調(diào)度和優(yōu)化,性能更高,消耗的CPU和GPU資源更少。
- 避免幀丟失現(xiàn)象,確?;卣{(diào)連續(xù)執(zhí)行,實現(xiàn)更流暢的動畫效果。
- 自動合并多個回調(diào),避免不必要的開銷。
- 與瀏覽器的刷新同步,不會在瀏覽器頁面不可見時執(zhí)行回調(diào)。
requestAnimationFrame的優(yōu)勢和適用場景
requestAnimationFrame
最適用于需要連續(xù)高頻執(zhí)行的動畫,如游戲開發(fā),數(shù)據(jù)可視化動畫等。它與瀏覽器刷新周期保持一致,不會因為間隔時間不均勻而導(dǎo)致動畫卡頓。
使用方法
requestAnimationFrame的基本語法
requestAnimationFrame
接收一個回調(diào)函數(shù)作為參數(shù),該回調(diào)函數(shù)會在瀏覽器下一次重繪前執(zhí)行。
const animation = () => { // 執(zhí)行動畫 requestAnimationFrame(animation); } requestAnimationFrame(animation);
上面代碼會不停調(diào)用requestAnimationFrame
,在每次瀏覽器重繪前執(zhí)行回調(diào)函數(shù),實現(xiàn)連續(xù)動畫效果。
如何在動畫中使用requestAnimationFrame
可以在回調(diào)函數(shù)里更新動畫的狀態(tài),然后清除上一幀,繪制新狀態(tài)的這一幀:
let angle = 0; const render = () => { ctx.clearRect(0, 0, width, height); // 清除上一幀 // 更新狀態(tài) angle += delta; ctx.beginPath(); ctx.arc(x, y, radius, 0, angle); ctx.stroke(); requestAnimationFrame(render); }
這樣通過在每次回調(diào)中更新參數(shù),就可以實現(xiàn)對象的連續(xù)動畫了。
requestAnimationFrame的回調(diào)函數(shù)參數(shù)
requestAnimationFrame
的回調(diào)函數(shù)會收到一個參數(shù),這個參數(shù)是一個時間戳,單位為毫秒,代表requestAnimationFrame
被觸發(fā)的時間。可以根據(jù)這個時間戳計算兩幀的時間間隔,來調(diào)整動畫速度。
let prevTimestamp; const render = timestamp => { if (!prevTimestamp) prevTimestamp = timestamp; const delta = timestamp - prevTimestamp; // 根據(jù)時間間隔計算速度 x += speed * delta; prevTimestamp = timestamp; requestAnimationFrame(render); }
性能優(yōu)化
避免在requestAnimationFrame回調(diào)函數(shù)中進行大量計算
由于requestAnimationFrame
的回調(diào)會在一個高優(yōu)先級的線程中被同步執(zhí)行,如果回調(diào)函數(shù)中有大量計算,會導(dǎo)致此線程被阻塞,從而引起頁面卡頓。
也就是如果 requestAnimationFrame
的回調(diào)函數(shù)執(zhí)行時間超過一幀(通常是 16.67 毫秒,因為瀏覽器通常以每秒約 60 幀的速度刷新),則可能會導(dǎo)致動畫性能下降,可能出現(xiàn)掉幀的情況,最終影響用戶體驗。這是因為瀏覽器每幀的時間是有限的,如果回調(diào)函數(shù)執(zhí)行時間過長,就會導(dǎo)致下一幀的準備和繪制時間受到壓縮,導(dǎo)致動畫卡頓。
通常,應(yīng)該盡量避免在 requestAnimationFrame
的回調(diào)函數(shù)中執(zhí)行耗時的操作。為了解決這個問題,可以采取以下一些策略:
- 優(yōu)化回調(diào)函數(shù): 使回調(diào)函數(shù)盡可能簡短,避免不必要的計算或循環(huán)。在回調(diào)中只執(zhí)行與動畫相關(guān)的必要操作。
- 分幀處理: 如果動畫需要處理大量數(shù)據(jù)或計算復(fù)雜的操作,可以將這些操作分散到多個
requestAnimationFrame
回調(diào)中,以避免長時間的占用。 - Web Workers: 將耗時的計算放在獨立的 Web Worker 線程中執(zhí)行,以不影響主線程和動畫渲染。
- 幀率控制: 如果回調(diào)函數(shù)耗時較長,可以根據(jù)回調(diào)函數(shù)的實際執(zhí)行時間來控制動畫的幀率。減小動畫對象的速度或者減少渲染質(zhì)量,以適應(yīng)當前性能。
- 監(jiān)測性能: 使用瀏覽器開發(fā)者工具來監(jiān)測性能,以找出哪些操作導(dǎo)致了回調(diào)函數(shù)執(zhí)行時間過長。
總之,確保 requestAnimationFrame
回調(diào)函數(shù)的執(zhí)行時間盡量短,以確保動畫的流暢性和性能。
使用硬件加速優(yōu)化動畫性能
啟用GPU
加速渲染,可以顯著提升動畫性能。
.animated { transform: translateZ(0); /* 開啟硬件加速 */ }
如何在不同設(shè)備上實現(xiàn)平滑的動畫效果
可根據(jù)requestAnimationFrame
回調(diào)的時間戳,計算這一幀與上一幀的間隔時間delta
,并根據(jù)delta
的值采取不同的優(yōu)化手段:
delta
特別小,說明這一幀花費時間過長,可能導(dǎo)致掉幀,可以降低動畫對象的移動速度或圖像質(zhì)量delta
逐漸變大,說明動畫逐漸卡頓,可以降低動畫對象數(shù)量或復(fù)雜度delta
波動較大,說明系統(tǒng)資源不足,可以采用簡單的動畫作為降級策略
與其他動畫庫的比較
requestAnimationFrame與setTimeout/setInterval的區(qū)別
setTimeout/setInterval
是固定時間間隔觸發(fā),requestAnimationFrame
依賴系統(tǒng)刷新調(diào)度setTimeout/setInterval
會無視頁面是否可見,requestAnimationFrame
會停止刷新setTimeout/setInterval
難以避免丟幀問題,requestAnimationFrame
與刷新同步避免丟幀
requestAnimationFrame與CSS動畫的結(jié)合使用
requestAnimationFrame
可用于更新動畫狀態(tài),實現(xiàn)復(fù)雜動畫邏輯,而CSS動畫用于聲明式定義動畫的樣式變化,兩者可配合實現(xiàn)更豐富的動畫效果。
實際應(yīng)用
使用requestAnimationFrame實現(xiàn)常見動畫效果
可使用 requestAnimationFrame
實現(xiàn)對象軌跡動畫、SVG圖形動畫、加載動畫等效果。
// 小球拖尾效果 const positions = []; const render = () => { // 添加新位置 positions.push({x, y}); if (positions.length > 10){ positions.shift(); } // 渲染小球 ctx.clearRect(0, 0, width, height); positions.forEach(pos => ctx.fillRect(pos.x, pos.y, 10, 10)); requestAnimationFrame(render); } // SVG繪制動畫 let length = 0; const render = () => { length += 4; svgLine.setAttribute("stroke-dasharray", length); if (length < 300) { requestAnimationFrame(render); } } requestAnimationFrame(render); // 進度條加載動畫 let progress = 0; const render = () => { progress += 1; loadBar.style.width = progress + '%'; if(progress < 100) { requestAnimationFrame(render); } } requestAnimationFrame(render);
requestAnimationFrame在游戲開發(fā)中的應(yīng)用
游戲需要非常流暢的畫面和實時的響應(yīng),這正是requestAnimationFrame
的優(yōu)勢,它可用于實現(xiàn)游戲中的物體移動、碰撞檢測、幀數(shù)控制等操作。
// 飛機射擊動畫 const update = () => { // 子彈位置更新 bullets.forEach(bullet => { bullet.position += speed; }) // 飛機位置更新 aircraft.position += delta * speed; // 繪制所有元素 render(bullets, aircraft); requestAnimationFrame(update); }
requestAnimationFrame在響應(yīng)式設(shè)計中的應(yīng)用
可使用requestAnimationFrame
來更平滑地執(zhí)行響應(yīng)式布局的變化,避免布局突然大幅移動帶來的視覺沖擊感。
let width = 500; const resize = () => { width = container.clientWidth; box.style.width = width + "px"; requestAnimationFrame(resize); } window.addEventListener("resize", resize);
兼容性和后續(xù)發(fā)展
requestAnimationFrame的瀏覽器兼容性
requestAnimationFrame
現(xiàn)在已經(jīng)得到了廣泛支持,可以直接使用。對于不支持的瀏覽器,可以用setTimeout
模擬requestAnimationFrame
。
window.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || (cb => setTimeout(cb, 1000/60));
requestAnimationFrame的未來發(fā)展趨勢
未來requestAnimationFrame
可能會支持設(shè)置幀率、增強調(diào)度算法等,提升動畫性能。Web工作者線程也可帶來更多優(yōu)化空間。
瀏覽器廠商也在繼續(xù)改進相關(guān)API,比如setTimeout
和requestIdleCallback
也在朝著更精確的調(diào)度方向發(fā)展。
如何在不支持requestAnimationFrame的瀏覽器中實現(xiàn)類似效果
可以通過自己實現(xiàn)一個polyfill
:
window.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || (cb => setTimeout(cb, 1000/60));
這樣就可以在大多數(shù)瀏覽器中使用 requestAnimationFrame
了。對于老舊瀏覽器,可以使用 setInterval
模擬,但效果會比較粗糙。
總結(jié)
requestAnimationFrame
是實現(xiàn)復(fù)雜動畫的好幫手,必須掌握其用法與優(yōu)化技巧,才能發(fā)揮其最大效用。同時結(jié)合其他技術(shù)如CSS動畫、Web Worker等也可以實現(xiàn)更好的性能。隨著瀏覽器的不斷進步,requestAnimationFrame
還具有很大的拓展?jié)摿Α?/p>
以上就是requestAnimationFrame用法優(yōu)化源碼解析的詳細內(nèi)容,更多關(guān)于requestAnimationFrame優(yōu)化的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解BootStrap表單驗證中重置BootStrap-select驗證提示不清除的坑
這篇文章主要介紹了詳解BootStrap表單驗證中重置BootStrap-select驗證提示不清除的坑,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09JavaScript設(shè)置、獲取、清除單值和多值cookie的方法
cookie 是存儲于訪問者的計算機中的變量。每當同一臺計算機通過瀏覽器請求某個頁面時,就會發(fā)送這個 cookie。你可以使用 JavaScript 來創(chuàng)建和取回 cookie 的值,本文通過一段代碼給大家介紹js設(shè)置、獲取、清除單值和多值cookie的方法,需要的朋友一起學(xué)習(xí)吧2015-11-11javascript算法題 求任意一個1-9位不重復(fù)的N位數(shù)在該組合中的大小排列序號
從1--9中選取N個數(shù)字,組成不重復(fù)的N位數(shù),從小到大進行編號,當輸入其中任何一個數(shù)M時,能找出該數(shù)字對應(yīng)的編號2012-07-07Some tips of wmi scripting in jscript (1)
Some tips of wmi scripting in jscript (1)...2007-04-04JavaScript XML和string相互轉(zhuǎn)化實現(xiàn)代碼
兩個小function實現(xiàn)XML和string相互轉(zhuǎn)化,需要的朋友可以參考下。2011-07-07