在前端做一些持續執行的動畫時,一般會通過setTimeOut去實現,其實js還有另一個API和setTimeout功能類似,就是requestAnimationFrame,在說requestAnimationFrame之前項說說setTimeOut和setInterval
setTimeout 和 setInterval區別
- setTimeout: 指定延期后調用函數,每次
setTimeout計到期到向事件隊列推入一個事件 - setInterval:以指定周期,向事件隊列推入一個事件
setInterval存在的一些問題:
定時器代碼可能在代碼再次被添加到隊列之前還沒有完成執行,結果導致定時器代碼連續運行好幾次,而之間沒有任何停頓。
而javascript引擎對這個問題的解決是:當使用setInterval()時,僅當沒有該定時器的任何其他代碼實例時,才將定時器代碼添加到隊列中。這確保了定時器代碼加入到隊列中的最小時間間隔為指定間隔。
但是,這樣會導致兩個問題:
- 1、某些間隔被跳過;
- 2、多個定時器的代碼執行之間的間隔可能比預期的小
使用setTimeout構造輪詢模擬定時器
在前一個定時器代碼執行完之前,不會向隊列插入新的定時器代碼,確保不會有任何缺失的間隔。而且,它可以保證在下一次定時器代碼執行之前,至少要等待指定的間隔,避免了連續的運行
setTimeout(function fn(){
console.log('我被調用了');
setTimeout(fn, 100);
},100);
requestAnimationFrame
requestAnimationFrame是瀏覽器用於定時循環操作的一個接口,類似於setTimeout,主要用途是按幀對網頁進行重繪。
顯示器有固定的刷新頻率(60Hz或75Hz),也就是說,每秒最多只能重繪60次或75次,requestAnimationFrame的基本思想就是與這個刷新頻率保持同步,利用這個刷新頻率進行頁面重繪。
此外,使用這個API,一旦頁面不處於瀏覽器的當前標簽,就會自動停止刷新。這就節省了CPU、GPU和電力。
requestAnimationFrame是在主線程上完成。這意味着,如果主線程非常繁忙,requestAnimationFrame的動畫效果會大打折扣。
requestAnimationFrame使用一個回調函數作為參數。這個回調函數會在瀏覽器重繪之前調用
requestID = window.requestAnimationFrame(callback);
兼容處理
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
如何使用
function animation(){ requestAnimFrame(animation); }
結束動畫
cancelAnimationFrame(animationId);
requestIdleCallback()
requestIdleCallback會在每次屏幕刷新時,判斷當前幀是否還有多余的時間,如果有,則會調用回調函數
利用這個特性,我們可以在動畫執行的期間,利用每幀的空閑時間來進行數據發送的操作,或者一些優先級比較低的操作,此時不會使影響到動畫的性能,或者和requestAnimationFrame搭配,可以實現一些頁面性能方面的的優化,
react 的
fiber架構也是基於requestIdleCallback實現的
總結
- 從單線程模型和任務隊列出發理解
setTimeout(fn, 0),並不是立即執行。 - JS 動畫用
requestAnimationFrame會比setInterval效果更好 requestIdleCallback()常用來切割長任務,利用空閑時間執行,避免主線程長時間阻塞。
