requestAnimationFrame 原理灾炭,使用

在Web應用中,實現(xiàn)動畫效果的方法比較多讹剔,Javascript 中可以通過定時器 setTimeout 來實現(xiàn)油讯,css3 可以使用 transition 和 animation 來實現(xiàn)详民,html5 中的 canvas 也可以實現(xiàn),svg也可以實現(xiàn)動畫陌兑。除此之外沈跨,html5 還提供一個專門用于請求動畫的API,那就是 requestAnimationFrame兔综,顧名思義就是請求動畫幀饿凛。 為了深入理解 requestAnimationFrame 背后的原理,我們首先需要了解一下與之相關的幾個概念:

1. 屏幕刷新頻率

即圖像在屏幕上更新的速度邻奠,也即屏幕上的圖像每秒鐘出現(xiàn)的次數(shù)笤喳,它的單位是赫茲(Hz)。 對于一般筆記本電腦碌宴,這個頻率大概是60Hz

市面上常見的顯示器有兩種杀狡,即CRT和LCD, CRT就是傳統(tǒng)顯示器贰镣,LCD就是我們常說的液晶顯示器呜象。

CRT是一種使用陰極射線管的顯示器,屏幕上的圖形圖像是由一個個因電子束擊打而發(fā)光的熒光點組成碑隆,由于顯像管內熒光粉受到電子束擊打后發(fā)光的時間很短恭陡,所以電子束必須不斷擊打熒光粉使其持續(xù)發(fā)光。電子束每秒擊打熒光粉的次數(shù)就是屏幕刷新頻率上煤。

而對于LCD來說休玩,則不存在刷新頻率的問題,它根本就不需要刷新劫狠。因為LCD中每個像素都在持續(xù)不斷地發(fā)光拴疤,直到不發(fā)光的電壓改變并被送到控制器中,所以LCD不會有電子束擊打熒光粉而引起的閃爍現(xiàn)象独泞。

因此呐矾,當你對著電腦屏幕什么也不做的情況下,顯示器也會以每秒60次的頻率正在不斷的更新屏幕上的圖像懦砂。為什么你感覺不到這個變化蜒犯? 那是因為人的眼睛有視覺停留效應,即前一副畫面留在大腦的印象還沒消失荞膘,緊接著后一副畫面就跟上來了罚随,這中間只間隔了16.7ms(1000/60≈16.7), 所以會讓你誤以為屏幕上的圖像是靜止不動的衫画。而屏幕給你的這種感覺是對的毫炉,試想一下,如果刷新頻率變成1次/秒削罩,屏幕上的圖像就會出現(xiàn)嚴重的閃爍瞄勾,這樣就很容易引起眼睛疲勞费奸、酸痛和頭暈目眩等癥狀。

2. 動畫原理

根據(jù)上面的原理我們知道进陡,你眼前所看到圖像正在以每秒60次的頻率刷新愿阐,由于刷新頻率很高,因此你感覺不到它在刷新趾疚。而動畫本質就是要讓人眼看到圖像被刷新而引起變化的視覺效果缨历,這個變化要以連貫的、平滑的方式進行過渡糙麦。 那怎么樣才能做到這種效果呢辛孵?

刷新頻率為60Hz的屏幕每16.7ms刷新一次,我們在屏幕每次刷新前赡磅,將圖像的位置向左移動一個像素魄缚,即1px。這樣一來焚廊,屏幕每次刷出來的圖像位置都比前一個要差1px冶匹,因此你會看到圖像在移動;由于我們人眼的視覺停留效應咆瘟,當前位置的圖像停留在大腦的印象還沒消失嚼隘,緊接著圖像又被移到了下一個位置,因此你才會看到圖像在流暢的移動袒餐,這就是視覺效果上形成的動畫飞蛹。

3. setTimeout

理解了上面的概念以后,我們不難發(fā)現(xiàn)灸眼,setTimeout 其實就是通過設置一個間隔時間來不斷的改變圖像的位置桩皿,從而達到動畫效果的。但我們會發(fā)現(xiàn)幢炸,利用seTimeout實現(xiàn)的動畫在某些低端機上會出現(xiàn)卡頓、抖動的現(xiàn)象拒贱。 這種現(xiàn)象的產生有兩個原因:

setTimeout的執(zhí)行時間并不是確定的宛徊。在Javascript中, setTimeout 任務被放進了異步隊列中逻澳,只有當主線程上的任務執(zhí)行完以后闸天,才會去檢查該隊列里的任務是否需要開始執(zhí)行,因此 setTimeout 的實際執(zhí)行時間一般要比其設定的時間晚一些斜做。

刷新頻率受屏幕分辨率和屏幕尺寸的影響苞氮,因此不同設備的屏幕刷新頻率可能會不同,而 setTimeout只能設置一個固定的時間間隔瓤逼,這個時間不一定和屏幕的刷新時間相同笼吟。

以上兩種情況都會導致setTimeout的執(zhí)行步調和屏幕的刷新步調不一致库物,從而引起丟幀現(xiàn)象。 那為什么步調不一致就會引起丟幀呢贷帮?

首先要明白戚揭,setTimeout的執(zhí)行只是在內存中對圖像屬性進行改變,這個變化必須要等到屏幕下次刷新時才會被更新到屏幕上撵枢。如果兩者的步調不一致民晒,就可能會導致中間某一幀的操作被跨越過去,而直接更新下一幀的圖像锄禽。假設屏幕每隔16.7ms刷新一次潜必,而setTimeout每隔10ms設置圖像向左移動1px, 就會出現(xiàn)如下繪制過程:

第0ms: 屏幕未刷新沃但,等待中磁滚,setTimeout也未執(zhí)行,等待中绽慈;

第10ms: 屏幕未刷新恨旱,等待中,setTimeout開始執(zhí)行并設置圖像屬性left=1px坝疼;

第16.7ms: 屏幕開始刷新搜贤,屏幕上的圖像向左移動了1px, setTimeout 未執(zhí)行钝凶,繼續(xù)等待中仪芒;

第20ms: 屏幕未刷新,等待中耕陷,setTimeout開始執(zhí)行并設置left=2px;

第30ms: 屏幕未刷新掂名,等待中,setTimeout開始執(zhí)行并設置left=3px;

第33.4ms:屏幕開始刷新哟沫,屏幕上的圖像向左移動了3px饺蔑, setTimeout未執(zhí)行,繼續(xù)等待中嗜诀;

從上面的繪制過程中可以看出猾警,屏幕沒有更新left=2px的那一幀畫面,圖像直接從1px的位置跳到了3px的的位置隆敢,這就是丟幀現(xiàn)象发皿,這種現(xiàn)象就會引起動畫卡頓。

4. requestAnimationFrame

setTimeout相比拂蝎,requestAnimationFrame最大的優(yōu)勢是由系統(tǒng)來決定回調函數(shù)的執(zhí)行時機穴墅。具體一點講,如果屏幕刷新率是60Hz,那么回調函數(shù)就每16.7ms被執(zhí)行一次,如果刷新率是75Hz玄货,那么這個時間間隔就變成了1000/75=13.3ms皇钞,換句話說就是,requestAnimationFrame的步伐跟著系統(tǒng)的刷新步伐走誉结。它能保證回調函數(shù)在屏幕每一次的刷新間隔中只被執(zhí)行一次鹅士,這樣就不會引起丟幀現(xiàn)象,也不會導致動畫出現(xiàn)卡頓的問題惩坑。

這個API的調用很簡單掉盅,如下所示:

var progress = 0;
//回調函數(shù)
function render() {
    progress += 1; //修改圖像的位置
    if (progress < 100) {
           //在動畫沒有結束前,遞歸渲染
           window.requestAnimationFrame(render);
    }
}
//第一幀渲染
window.requestAnimationFrame(render);

除此之外以舒,requestAnimationFrame還有以下兩個優(yōu)勢:

  • CPU節(jié)能:使用setTimeout實現(xiàn)的動畫趾痘,當頁面被隱藏或最小化時,setTimeout 仍然在后臺執(zhí)行動畫任務蔓钟,由于此時頁面處于不可見或不可用狀態(tài)永票,刷新動畫是沒有意義的,完全是浪費CPU資源滥沫。而requestAnimationFrame則完全不同侣集,當頁面處理未激活的狀態(tài)下,該頁面的屏幕刷新任務也會被系統(tǒng)暫停兰绣,因此跟著系統(tǒng)步伐走的requestAnimationFrame也會停止渲染世分,當頁面被激活時,動畫就從上次停留的地方繼續(xù)執(zhí)行缀辩,有效節(jié)省了CPU開銷臭埋。
  • 函數(shù)節(jié)流:在高頻率事件(resize,scroll等)中,為了防止在一個刷新間隔內發(fā)生多次函數(shù)執(zhí)行臀玄,使用requestAnimationFrame可保證每個刷新間隔內瓢阴,函數(shù)只被執(zhí)行一次,這樣既能保證流暢性健无,也能更好的節(jié)省函數(shù)執(zhí)行的開銷荣恐。一個刷新間隔內函數(shù)執(zhí)行多次時沒有意義的,因為顯示器每16.7ms刷新一次累贤,多次繪制并不會在屏幕上體現(xiàn)出來募胃。

5.優(yōu)雅降級

由于requestAnimationFrame目前還存在兼容性問題,而且不同的瀏覽器還需要帶不同的前綴畦浓。因此需要通過優(yōu)雅降級的方式對requestAnimationFrame進行封裝,優(yōu)先使用高級特性检疫,然后再根據(jù)不同瀏覽器的情況進行回退讶请,直止只能使用setTimeout的情況。下面的代碼就是有人在github上提供的polyfill,詳細介紹請參考github代碼 requestAnimationFramehttps://github.com/darius/requestAnimationFrame

if (!Date.now)
    Date.now = function() { return new Date().getTime(); };
(function() {
    'use strict';
    var vendors = ['webkit', 'moz'];
    for (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) {
        var vp = vendors[i];
        window.requestAnimationFrame = window[vp+'RequestAnimationFrame'];
        window.cancelAnimationFrame = (window[vp+'CancelAnimationFrame']
                                   || window[vp+'CancelRequestAnimationFrame']);
    }
    if (/iP(ad|hone|od).*OS 6/.test(window.navigator.userAgent) // iOS6 is buggy
        || !window.requestAnimationFrame || !window.cancelAnimationFrame) {
        var lastTime = 0;
        window.requestAnimationFrame = function(callback) {
            var now = Date.now();
            var nextTime = Math.max(lastTime + 16, now);
            return setTimeout(function() { callback(lastTime = nextTime); },
                              nextTime - now);
        };
        window.cancelAnimationFrame = clearTimeout;
    }
}());

轉至: http://www.cnblogs.com/onepixel/p/7078617.html

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末夺溢,一起剝皮案震驚了整個濱河市论巍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌风响,老刑警劉巖嘉汰,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異状勤,居然都是意外死亡鞋怀,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門持搜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來密似,“玉大人,你說我怎么就攤上這事葫盼〔须纾” “怎么了?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵贫导,是天一觀的道長抛猫。 經(jīng)常有香客問我,道長孩灯,這世上最難降的妖魔是什么闺金? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮钱反,結果婚禮上掖看,老公的妹妹穿的比我還像新娘。我一直安慰自己面哥,他們只是感情好哎壳,可當我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著尚卫,像睡著了一般归榕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上吱涉,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天刹泄,我揣著相機與錄音,去河邊找鬼怎爵。 笑死特石,一個胖子當著我的面吹牛,可吹牛的內容都是我干的鳖链。 我是一名探鬼主播姆蘸,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了逞敷?” 一聲冷哼從身側響起狂秦,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎推捐,沒想到半個月后裂问,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡牛柒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年堪簿,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片焰络。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡戴甩,死狀恐怖,靈堂內的尸體忽然破棺而出闪彼,到底是詐尸還是另有隱情甜孤,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布畏腕,位于F島的核電站缴川,受9級特大地震影響,放射性物質發(fā)生泄漏描馅。R本人自食惡果不足惜把夸,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望铭污。 院中可真熱鬧恋日,春花似錦、人聲如沸嘹狞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽磅网。三九已至谈截,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間涧偷,已是汗流浹背簸喂。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留燎潮,地道東北人喻鳄。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像确封,于是被迫代替她去往敵國和親除呵。 傳聞我的和親對象是個殘疾皇子唉锌,可洞房花燭夜當晚...
    茶點故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內容

  • 在Web應用中,實現(xiàn)動畫效果的方法比較多竿奏,Javascript 中可以通過定時器 setTimeout 來實現(xiàn),c...
    流動碼文閱讀 604評論 0 0
  • 看了很多視頻腥放、文章泛啸,最后卻通通忘記了,別人的知識依舊是別人的秃症,自己卻什么都沒獲得候址。此系列文章旨在加深自己的印象,因...
    DCbryant閱讀 720評論 0 2
  • 在iOS中隨處都可以看到絢麗的動畫效果种柑,實現(xiàn)這些動畫的過程并不復雜岗仑,今天將帶大家一窺ios動畫全貌。在這里你可以看...
    每天刷兩次牙閱讀 8,471評論 6 30
  • ??隨著 HTML5 的出現(xiàn),面向未來 Web 應用的 JavaScript API 也得到了極大的發(fā)展驶赏。這些 A...
    霜天曉閱讀 1,021評論 0 0
  • 從JS執(zhí)行機制說起 瀏覽器(或者說JS引擎)執(zhí)行JS的機制是基于事件循環(huán)炸卑。 由于JS是單線程,所以同一時間只能執(zhí)行...
    love2013閱讀 868評論 0 1