深入理解 requestAnimationFrame

  • 2020 年 11 月 30 日
  • 筆記

深入理解 requestAnimationFrame

Web應用中,實現動畫效果的方法如下:

  • JavaScript中可以通過setTimeout來實現
  • CSS3中可以通過transition和animation來實現
  • html5的canvas也可以實現
  • html5新增的請求動畫API,requestAnimationFrame請求幀動畫

requestAnimationFrame 背後的原理

螢幕刷新頻率

影像在螢幕上的更新速度,也即螢幕上的影像每秒鐘出現的次數,它的單位是赫茲(Hz)。
一般電子產品是60HZ

顯示器有兩種 : CRT和LCD

CRT是一種使用陰極射線管的顯示器,螢幕上的圖形影像是由一個個因電子束擊打而發光的熒光點組成,由於顯像管內熒光粉受到電子束擊打後發光的時間很短,所以電子束必須不斷擊打熒光粉使其持續發光。電子束每秒擊打熒光粉的次數就是螢幕刷新頻率。

而對於LCD來說,則不存在刷新頻率的問題,它根本就不需要刷新。因為LCD中每個像素都在持續不斷地發光,直到不發光的電壓改變並被送到控制器中,所以LCD不會有電子束擊打熒光粉而引起的閃爍現象.

一秒鐘刷新60次

1s = 1000ms;

1000ms/60 ≈ 16.7ms/幀

動畫的原理

動畫的本質 :讓人眼看到影像被刷新而引起變化的視覺效果,這個變化要以連貫的、平滑的方式進行過渡

setTimeout

setTimeout 其實就是通過設置一個間隔時間來不斷的改變影像的位置,從而達到動畫效果的。但我們會發現,利用seTimeout實現的動畫在某些低端機上會出現卡頓、抖動的現象。 這種現象的產生有兩個原因 :

  • setTimeout的執行時間並不是確定的。在Javascript中, setTimeout任務被放進了非同步隊列中,只有當主執行緒上的任務執行完以後,才會去檢查該隊列里的任務是否需要開始執行,因此setTimeout的實際執行時間一般要比其設定的時間晚一些。

  • 刷新頻率受螢幕解析度和螢幕尺寸的影響,因此不同設備的螢幕刷新頻率可能會不同,而 setTimeout只能設置一個固定的時間間隔,這個時間不一定和螢幕的刷新時間相同。

導致的結果 : setTimeout的執行步調和螢幕的刷新步調不一致,從而引起丟幀現象

毫秒數 螢幕刷新時間(16.7ms) setTimeout延遲時間(10ms)假設
第0ms 螢幕未刷新 setTimeout未啟動
第10ms 螢幕未刷新 setTimeout開始執行 (left =1)
第16.7m 螢幕開始刷新(left = 1) setTimeout未執行 (left =1)
第20ms 螢幕未刷新(left = 1) setTimeout開始執行 (left =2)
第30ms 螢幕未刷新(left = 1) setTimeout開始執行(left = 3);
第33.4ms 螢幕開始刷新(left = 2) setTimeout未執行 (left =3);

我們可以看出螢幕並沒有更新left = 2的那一幀動畫,影像直接從1px跳到3px,丟幀的現象就會導致卡頓

requestAnimationFrame

requestAnimationFrame最大的優勢是由系統來決定回調函數的執行時機,也就是會隨著客戶端顯示器的的刷新率變化而變化

requestAnimationFrame 優勢

CPU節能:使用setTimeout實現的動畫,當頁面被隱藏或最小化時,setTimeout 仍然在後台執行動畫任務,由於此時頁面處於不可見或不可用狀態,刷新動畫是沒有意義的,完全是浪費CPU資源。而requestAnimationFrame則完全不同,當頁面處理未激活的狀態下,該頁面的螢幕刷新任務也會被系統暫停,因此跟著系統步伐走的requestAnimationFrame也會停止渲染,當頁面被激活時,動畫就從上次停留的地方繼續執行,有效節省了CPU開銷。

函數節流:在高頻率事件(resize,scroll等)中,為了防止在一個刷新間隔內發生多次函數執行,使用requestAnimationFrame可保證每個刷新間隔內,函數只被執行一次,這樣既能保證流暢性,也能更好的節省函數執行的開銷。一個刷新間隔內函數執行多次時沒有意義的,因為顯示器每16.7ms刷新一次,多次繪製並不會在螢幕上體現出來。