神奇的requestAnimationFrame解決傳統定時器bug

可能你還沒見過這個東西是個啥,其實他就是類似於setTimeout和setInterval,然而它與setTimeout和setInterval又有所不同,requestAnimationFrame不需要設置時間間隔。這有什麼好處呢?requestAnimationFrame有何神奇之處?本文將詳細介紹HTML5新增的定時器requestAnimationFrame。

計時器一直是javascript動畫的核心技術。而編寫動畫循環的關鍵是要知道延遲時間多長合適。一方面,循環間隔必須足夠短,這樣才能讓不同的動畫效果顯得平滑流暢;另一方面,循環間隔還要足夠長,這樣才能確保瀏覽器有能力渲染產生的變化。

大多數電腦顯示器的刷新頻率是60Hz,大概相當於每秒鐘重繪60次。大多數瀏覽器都會對重繪操作加以限制,不超過顯示器的重繪頻率,因爲即使超過那個頻率用戶體驗也不會有提升。因此,最平滑動畫的最佳循環間隔是1000ms/60,約等於16.6ms。

而setTimeout和setInterval的問題是,它們都不精確。它們的內在運行機制決定了時間間隔參數實際上只是指定了把動畫代碼添加到瀏覽器UI線程隊列中以等待執行的時間。如果隊列前面已經加入了其他任務,那動畫代碼就要等前面的任務完成後再執行。

requestAnimationFrame採用系統時間間隔,保持最佳繪製效率,不會因爲間隔時間過短,造成過度繪製,增加開銷;也不會因爲間隔時間太長,使用動畫卡頓不流暢,讓各種網頁動畫效果能夠有一個統一的刷新機制,從而節省系統資源,提高系統性能,改善視覺效果。

特點

  • 【1】requestAnimationFrame會把每一幀中的所有DOM操作集中起來,在一次重繪或迴流中就完成,並且重繪或迴流的時間間隔緊緊跟隨瀏覽器的刷新頻率
  • 【2】在隱藏或不可見的元素中,requestAnimationFrame將不會進行重繪或迴流,這當然就意味着更少的CPU、GPU和內存使用量
  • 【3】requestAnimationFrame是由瀏覽器專門爲動畫提供的API,在運行時瀏覽器會自動優化方法的調用,並且如果頁面不是激活狀態下的話,動畫會自動暫停,有效節省了CPU開銷
  • 【4】requestAnimationFrame可以在某些時候解決setTimeout和setInterval的bug,傳統定時器在頁面tab切換時候,定時器會進入一個假死狀態,比如你電腦開了兩個窗口,首先你打開有定時器的頁面,然後定時器開始執行,假設每秒i+1,當i=10時候你切換到別的tab窗口,隔了一段時間看,i繼續從=10開始累加。。。

使用

requestAnimationFrame的用法與settimeout很相似,只是不需要設置時間間隔而已。requestAnimationFrame使用一個回調函數作爲參數,這個回調函數會在瀏覽器重繪之前調用。它返回一個整數,表示定時器的編號,這個值可以傳遞給cancelAnimationFrame用於取消這個函數的執行

1
2
3
4
5
var timer = requestAnimationFrame(function(){
    console.log(0);
});
console.log(timer);
//結果1和0

上面這段代碼其中結果1則是requestAnimationFrame返回值,執行一遍返回1,如此類推,你可能疑惑爲什麼console.log(timer);中的timer沒有加()也能執行,這得回到文章頭部說到的你可以理解爲requestAnimationFrame就是個定時器,定時器自然不需要手動去觸發執行

cancelAnimationFrame方法用於取消定時器

1
2
3
4
5
//控制檯什麼都不輸出
var timer = requestAnimationFrame(function(){
    console.log(0);
});
cancelAnimationFrame(timer);

也可以直接使用返回值進行取消

1
2
3
4
var timer = requestAnimationFrame(function(){
    console.log(0);
});
cancelAnimationFrame(1);

示例,解決傳統定時器bug

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
   * 倒計時循環
   * @private
   */
  _CountDownLoop({
    var currStemp new Date();
    currStemp new Date(currStemp.getTime(+ this.offset);
    //如果結束時間戳減去當前時間時間戳小於等於0則設置倒計時結束標識爲true
    if (this.endStemp.getTime(- currStemp.getTime(<= 0{
      this.isEnd true;
    }
    //如果結束則調用結束回調
    if (this.isEnd === true{
      // console.log('countdown end');
      this.endcallback.apply(this[this.endurl]);
    else {
      this._render(currStemp);
      var that = this;
      requestAnimationFrame(function({
        that._CountDownLoop();
      });
    }
  }

小結

上面示例中我們可以看到,通過requestAnimationFrame不斷的自己調用自己,實現高頻度刷新倒計時,從而解決了頁面切換窗口等傳統setTimeout和setInterval假死問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章