深入理解requestAnimationFrame的動畫循環(huán)

一、初識requestAnimationFrame

requestAnimationFrame解決了瀏覽器不知道javascript動畫什么時候開始攻晒、不知道最佳循環(huán)間隔時間的問題匾旭。它是跟著瀏覽器的繪制走的鹰溜,如果瀏覽器繪制間隔是16.7ms,它就按這個間隔繪制;如果瀏覽器繪制間隔是10ms, 它就按10ms繪制褪那。這樣就不會存在過度繪制的問題,動畫不會丟幀式塌。

內部是這么運作的:

瀏覽器頁面每次要重繪博敬,就會通知requestAnimationFrame;
這是資源非常高效的一種利用方式。

有以下兩點:

1峰尝、就算很多個requestAnimationFrame()要執(zhí)行偏窝,瀏覽器只要通知一次就可以了。而setTimeout是多個獨立繪制武学。

2祭往、一旦頁面不在當前頁面(比如:頁面最小化了),頁面是不會進行重繪的火窒,自然requestAnimationFrame也不會觸發(fā)(因為沒有通知)硼补。頁面繪制全部停止,資源高效利用熏矿。

二. 動畫的循環(huán)間隔

編寫動畫循環(huán)的關鍵已骇,是要知道延遲時間多長合適。一方面票编,循環(huán)時間必須足夠短褪储,這樣才能保證動畫效果更平滑流暢;另一方面慧域,循環(huán)還要足夠長鲤竹,這樣才能保證瀏覽器有能力渲染產生的變化。大多數(shù)顯示器的刷新頻率是60Hz昔榴,相當于每秒鐘重繪60次宛裕。大多數(shù)瀏覽器都會對重繪操作加以限制,不超過顯示器的重繪頻率论泛,因為即使超過了這個頻率揩尸,用戶體驗也不會有提升。

因此最平滑動畫的最佳循環(huán)間隔是1000ms/60屁奏,約等于17ms岩榆。以這個循環(huán)間隔重繪的動畫是平滑的,因為這個速度最接近瀏覽器的最高限速。為了適應17ms的循環(huán)間隔勇边,多重動畫可能需要加以節(jié)制犹撒,以便不會完成得太快。

雖然與使用多組setTimeout()相比粒褒,使用setInterval()的動畫循環(huán)效率更高识颊。但是無論setTimeout()還是setInterval()都不十分精確。為它們傳入的第二個參數(shù)奕坟,實際上只是指定了把動畫代碼添加到瀏覽器UI線程隊列以等待執(zhí)行的時間祥款。如果隊列前面已經加入了其他任務,那動畫代碼就要等前面的任務執(zhí)行完成后再執(zhí)行月杉。如果UI線程繁忙刃跛,比如忙于處理用戶操作,那么即使把代碼加入隊列也不會立即執(zhí)行苛萎。

因此桨昙,知道什么時候繪制下一幀是保證動畫平滑的關鍵。然而腌歉,面對不十分精確的setTimeout()setInterval()蛙酪,開發(fā)人員至今都沒有辦法確保瀏覽器按時繪制下一幀。

以下是幾個瀏覽器的計時器精度

  IE8及其以下版本瀏覽器: 15.6ms翘盖;

  IE9及其以上版本瀏覽器:4ms桂塞;

  Firefox和Safari:10ms;

  Chrome:4ms最仑。

更為復雜的是藐俺,瀏覽器開始限制后臺標簽頁或不活動標簽頁的計數(shù)器。因此泥彤,即使你優(yōu)化了循環(huán)間隔欲芹,可能仍然只能接近你想要的效果。

三. requestAnimationFrame()

Mozilla的 Robert O'Callahan 指出吟吝,CSS變換動畫的優(yōu)勢在于瀏覽器知道動畫什么時候開始菱父,因此會計算出正確的循環(huán)間隔,在適當?shù)臅r候刷新UI剑逃。而對于JavaScript動畫浙宜,瀏覽器就無從知曉什么時候開始。

因此Robert O'Callahan的方案是蛹磺,創(chuàng)建一個新方法mozRequestAnimationFrame() 粟瞬,通過它告訴瀏覽器某些代碼將要執(zhí)行動畫。這樣瀏覽器可以在運行某些代碼后進行適當?shù)膬?yōu)化萤捆。

setTimeout()setInterval()方法不同裙品,requestAnimationFrame()不需要調用者指定幀速率俗批,瀏覽器會自行決定最佳的幀效率。

requestAnimationFrame()方法接收一個參數(shù)市怎,即在重繪屏幕前調用以個函數(shù)岁忘。這個函數(shù)負責改變下一次重繪時的DOM樣式。為了創(chuàng)建動畫循環(huán)区匠,可以像使用setTimeout()一樣干像,把多個對requestAnimationFrame()的調用連綴起來。

如:

function drawFrame() {

    window.requestAnimationFrame(drawFrame);

    // animation code...

}

window.requestAnimationFrame(drawFrame);

四. requestAnimationFrame()的兼容性

4.1 requestAnimationFrame()的兼容性封裝:

由于mozRequestAnimationFrame()是HTML5的新功能,目前各大瀏覽器的支持情況各異。如果希望代碼具備更好的跨平臺性,可以考慮使用下面的代碼實現(xiàn)各平臺兼容性:

if(!window.requestAnimationFrame) {

    window.requestAnimationFrame = (window.webkitRequestAnimationFrame ||

    window.mozRequestAnimationFrame ||

    window.oRequestAnimationFrame ||

    window.msRequestAnimationFrame ||

        function(callback) {

            let self =this, start, finish;

            return window.setTimeout(function() {

            start = + new Date();

            callback(start);

            finish = + new Date();

            self.timeout = 1000/60 - (finish - start);

        }, self.timeout);

    });

}

這段代碼先檢查了window.requestAnimationFrame函數(shù)的定義是否存在。如果不存在漏健,就遍歷已知的各種瀏覽器實現(xiàn)并替代該函數(shù)。如果還是找不到一個與瀏覽器相關的實現(xiàn)雹熬,它最終會采用基于JavaScript定時器的動畫以每秒60幀的間隔調用setTimeout函數(shù)敬惦。

mozRequestAnimationFrame()會接收一個時間碼(從1970年1月1日起至今的毫秒數(shù)),表示下一次重繪的實際發(fā)生時間辅愿。這樣智亮,mozRequestAnimationFrame()就會根據(jù)這個時間碼設定將來的某個時刻進行重繪。

但是webkitRequestAnimationFrame()msRequestAnimationFrame()不會給回調函數(shù)傳遞時間碼点待,因此無法知道下一次重繪將發(fā)生在什么時間阔蛉。

如果要計算兩次重繪的時間間隔,F(xiàn)irefox中可以使用既有的時間碼癞埠,而在Chrome和IE則可以使用不太精確地Date()對象状原。

4.2 cancelRequestAnimFrame()的兼容性封裝:

W3C也提供了cancelRequestAnimationFrame()方法,用于取消回調函數(shù)苗踪。requestAnimationFrame()方法會返回一個對象颠区,用做標識回掉函數(shù)身份。若要取消回調函數(shù)的執(zhí)行通铲,可將其傳給cancelRequestAnimationFrame()毕莱。

window.cancelRequestAnimFrame = (function() {

    return window.cancelAnimationFrame ||

        window.webkitCancelRequestAnimationFrame ||

        window.mozCancelRequestAnimationFrame ||

        window.oCancelRequestAnimationFrame ||

        window.msCancelRequestAnimationFrame ||

        clearTimeout;

} )();

4.3 requestAnimationFrame()升級版封裝方法:

另外還有一種更優(yōu)雅的requestAnimationFrame()的兼容性封裝方法:

(function() {

    let lastTime = 0;

    let vendors = ['ms', 'moz', 'webkit', 'o'];

    for(let 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, element) {

        let currTime = new Date().getTime();

        let timeToCall = Math.max(0, 1000/60 - (currTime - lastTime));

        let id = window.setTimeout(function() { 

           callback(currTime + timeToCall); },

        timeToCall);

        lastTime = currTime + timeToCall;

        return id;

    };

    if(!window.cancelAnimationFrame)

    window.cancelAnimationFrame =function(id) {

        clearTimeout(id);

    };

}());
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市颅夺,隨后出現(xiàn)的幾起案子朋截,更是在濱河造成了極大的恐慌,老刑警劉巖吧黄,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件部服,死亡現(xiàn)場離奇詭異,居然都是意外死亡拗慨,警方通過查閱死者的電腦和手機廓八,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門厦酬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人瘫想,你說我怎么就攤上這事仗阅。” “怎么了国夜?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵减噪,是天一觀的道長。 經常有香客問我车吹,道長筹裕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任窄驹,我火速辦了婚禮朝卒,結果婚禮上,老公的妹妹穿的比我還像新娘乐埠。我一直安慰自己抗斤,他們只是感情好,可當我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布丈咐。 她就那樣靜靜地躺著瑞眼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪棵逊。 梳的紋絲不亂的頭發(fā)上伤疙,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天,我揣著相機與錄音辆影,去河邊找鬼徒像。 笑死,一個胖子當著我的面吹牛蛙讥,可吹牛的內容都是我干的锯蛀。 我是一名探鬼主播,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼键菱,長吁一口氣:“原來是場噩夢啊……” “哼谬墙!你這毒婦竟也來了?” 一聲冷哼從身側響起经备,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤拭抬,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后侵蒙,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體造虎,經...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年纷闺,在試婚紗的時候發(fā)現(xiàn)自己被綠了算凿。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片份蝴。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖氓轰,靈堂內的尸體忽然破棺而出婚夫,到底是詐尸還是另有隱情,我是刑警寧澤署鸡,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布案糙,位于F島的核電站,受9級特大地震影響靴庆,放射性物質發(fā)生泄漏时捌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一炉抒、第九天 我趴在偏房一處隱蔽的房頂上張望奢讨。 院中可真熱鬧,春花似錦焰薄、人聲如沸拿诸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽佳镜。三九已至僚稿,卻和暖如春凡桥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蚀同。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工缅刽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蠢络。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓衰猛,卻偏偏與公主長得像,于是被迫代替她去往敵國和親刹孔。 傳聞我的和親對象是個殘疾皇子啡省,可洞房花燭夜當晚...
    茶點故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內容