動(dòng)態(tài)網(wǎng)站建設(shè)與維護(hù)唯尚廣告聯(lián)盟平臺(tái)
requestAnimationFrame
window.requestAnimationFrame()
方法告訴瀏覽器您希望執(zhí)行動(dòng)畫并請(qǐng)求瀏覽器在下一次重繪之前調(diào)用指定的函數(shù)來(lái)更新動(dòng)畫。該方法使用一個(gè)回調(diào)函數(shù)作為參數(shù),這個(gè)回調(diào)函數(shù)會(huì)在瀏覽器重繪之前調(diào)用。
?? 注意:若您想要在下一次重繪時(shí)產(chǎn)生另一個(gè)動(dòng)畫畫面,您的回調(diào)例程必須調(diào)用
requestAnimationFrame()
。
傳統(tǒng)動(dòng)畫渲染的弊端
傳統(tǒng)的動(dòng)畫渲染是通過 setTimeout 和 setInterval 進(jìn)行實(shí)現(xiàn),但是這兩種定時(shí)器會(huì)有兩個(gè)弊端:
- 動(dòng)畫的時(shí)間間隔不好確定,設(shè)置時(shí)間過長(zhǎng)會(huì)使得動(dòng)畫不夠平滑流暢,設(shè)置過短會(huì)令瀏覽器的重繪頻率容易達(dá)到瓶頸(推薦最佳循環(huán)間隔是 17ms,因?yàn)榇蠖鄶?shù)電腦的顯示器刷新頻率是 60Hz,1000ms/60)。
- 定時(shí)器的第二個(gè)時(shí)間參數(shù)只是指定了多久后將動(dòng)畫任務(wù)添加到瀏覽器的 UI 線程隊(duì)列中,如果 UI 線程處于忙碌狀態(tài),那么動(dòng)畫不會(huì)立即執(zhí)行。
語(yǔ)法
requestAnimationFrame
window.requestAnimationFrame(callback);
參數(shù) | 說(shuō)明 | 類型 |
---|---|---|
callback | 下次重新繪制動(dòng)畫時(shí)調(diào)用的回調(diào)函數(shù)。該回調(diào)函數(shù)只有一個(gè)參數(shù) DOMHighResTimeStamp ,指示 requestAnimationFrame() 開始出發(fā)回調(diào)函數(shù)的當(dāng)前時(shí)間。 | function |
返回值 | 類型 |
---|---|
請(qǐng)求動(dòng)畫渲染的標(biāo)識(shí) ID。是個(gè)非零值,沒有其他意義??捎米?window.cancelAnimationFrame() 以取消回調(diào)函數(shù)。 | number 整數(shù) |
cancelAnimationFrame
window.cancelAnimationFrame(requestID);
參數(shù) | 說(shuō)明 | 類型 |
---|---|---|
requestId | 指定動(dòng)畫渲染的標(biāo)識(shí)符 | number |
優(yōu)點(diǎn)
requestAnimationFrame
會(huì)把每一幀中的所有 DOM 操作集中起來(lái),在一次重繪或回流中就完成,并且重繪或回流的時(shí)間間隔緊緊跟隨瀏覽器的刷新頻率- 在隱藏或不可見的元素中,或者瀏覽器標(biāo)簽頁(yè)不可見時(shí),
requestAnimationFrame
將不會(huì)進(jìn)行重繪或回流,這當(dāng)然就意味著更少的 CPU、GPU 和內(nèi)存使用量 requestAnimationFrame
是由瀏覽器專門為當(dāng)年規(guī)劃提供的 API,在運(yùn)行時(shí)瀏覽器會(huì)自動(dòng)優(yōu)化方法的調(diào)用,并且如果頁(yè)面不是激活狀態(tài)下的話,動(dòng)畫會(huì)自動(dòng)暫停,有效節(jié)省了 CPU 開銷
Firefox
、Chrome
、IE10+
對(duì) requestAnimationFrame
支持很好,但不兼容 IE9-
瀏覽器,但是我們可以用定時(shí)器完成兼容性改造。
(function () {var lastTime = 0;var vendors = ['webkit', 'moz'];for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];window.cancelAnimationFrame =window[vendors[x] + 'CancelAnimationFrame'] ||window[vendors[x] + 'CancelRequestAnimationFrame'];}if (!window.requestAnimationFrame)window.requestAnimationFrame = function (callback) {/*調(diào)整時(shí)間,讓一次動(dòng)畫等待和執(zhí)行時(shí)間在最佳循環(huán)時(shí)間間隔內(nèi)完成*/var currTime = new Date().getTime();var timeToCall = Math.max(0, 17 - (currTime - lastTime));var id = window.setTimeout(function () {callback(currTime + timeToCall);}, timeToCall);lastTime = currTime + timeToCall;return id;};if (!window.cancelAnimationFrame)window.cancelAnimationFrame = function (id) {clearTimeout(id);};
})();
傳遞參數(shù)
function requestAnimation(a, b, c) {if () {window.requestAnimationFrame(function () {requestAnimation(a, b, c)})}
}
requestIdleCallback
一般瀏覽器的刷新率為 60HZ,即 1 秒鐘刷新 60 次。1000ms / 60hz = 16.6
,大概每過 16.6ms 瀏覽器會(huì)渲染一幀畫面。
在這段時(shí)間內(nèi),瀏覽器大體會(huì)做兩件事:task 與 render。
task -> requestAnimationFrame -> render -> requestIdleCallback
如果渲染完成后還有空閑時(shí)間,則 requestIdleCallback API 會(huì)被調(diào)用。
掉幀與時(shí)間切片
如果 task
執(zhí)行時(shí)間超過了 16.6ms(比如 task 中有個(gè)很耗時(shí)的 while
循環(huán))。
那么這一幀就沒有時(shí)間 render
,頁(yè)面直到下一幀 render
后才會(huì)更新。表現(xiàn)為頁(yè)面卡頓一幀,或者說(shuō)掉幀。
最好的辦法是時(shí)間切片,把長(zhǎng)時(shí)間 task 分割為幾個(gè)短時(shí)間 task。
為了解決掉幀造成的卡頓,React16 將遞歸的構(gòu)建方式改為可中斷的遍歷。React16 就是基于 requestIdleCallbackAPI,實(shí)現(xiàn)了自己的 Fiber Reconciler。
以 5ms
的執(zhí)行時(shí)間劃分 task,每遍歷完一個(gè)節(jié)點(diǎn),就檢查當(dāng)前 task 是否已經(jīng)執(zhí)行了 5ms
。
如果超過 5ms
,則中斷本次 task
。
通過將 task
執(zhí)行時(shí)間切分為一個(gè)個(gè)小段,減少長(zhǎng)時(shí)間task
造成無(wú)法 render
的情況,這就是時(shí)間切片。