如何優化動畫??

動畫基礎知識

1. 動畫幀率(FPS)計算

幀的定義:1幅畫就叫作「1幀」,每秒幀數指的就是「每秒播放的畫面數」。每一幀都是靜止的圖象,快速連續地顯示幀便造成了運動的假象。高的幀率能夠獲得更流暢、更逼真的動畫。每秒鐘幀數 (fps) 愈多,所顯示的動做就會愈流暢。css

理論上說,FPS 越高,動畫會越流暢,目前大多數設備的屏幕刷新率爲 60 次/秒,因此一般來說 FPS 爲 60 frame/s 時動畫效果最好,也就是每幀的消耗時間爲 16.67ms。css3

直觀感覺,不一樣幀率的體驗:git

  • 幀率可以達到 50 ~ 60 FPS 的動畫將會至關流暢,讓人倍感溫馨;
  • 幀率在 30 ~ 50 FPS 之間的動畫,因各人敏感程度不一樣,溫馨度因人而異;
  • 幀率在 30 FPS 如下的動畫,讓人感受到明顯的卡頓和不適感;
  • 幀率波動很大的動畫,亦會令人感受到卡頓。

計算幀率的方法:github

  • 藉助 Chrome 開發者工具 - FPS meter
  • 使用 requestAnimationFrame 計算 FPS 原理,算法總有6種,詳情請看 -》blog.csdn.net/allen807733…

2. setTimeout 與 requestAnimationFrame

setTimeout出現的問題:web

  1. 根據 event loop 的規則,setTimeout 會被推到"任務隊列"掛起,必須等到當前代碼(執行棧)執行完,主線程纔會去執行它指定的回調函數。要是當前代碼耗時很長,有可能要等好久,因此並無辦法保證,回調函數必定會在setTimeout()指定的時間執行。
  2. HTML5標準規定了setTimeout()的第二個參數的最小值(最短間隔),不得低於4毫秒,若是低於這個值,就會自動增長。在此以前,老版本的瀏覽器都將最短間隔設爲10毫秒。另外,對於那些DOM的變更(尤爲是涉及頁面從新渲染的部分),一般不會當即執行,而是每16毫秒執行一次。這時使用requestAnimationFrame()的效果要好於setTimeout()。

例子:算法

var FPS = 60;
setTimeout(draw, 1000/FPS);
複製代碼

上述代碼,若是draw帶有大量邏輯計算,致使計算時間超過幀等待時間時,將會出現丟幀。除外,若是FPS過高,超過了當時瀏覽器的重繪頻率,將會形成計算浪費,例如瀏覽器實際才重繪2幀,但卻計算了3幀,那麼有1幀的計算就浪費了。canvas

引入requestAnimationFrame,這個方法是用來在頁面重繪以前,通知瀏覽器調用一個指定的函數,以知足開發者操做動畫的需求。segmentfault

var fps = 30;
var now;
var then = Date.now();
var interval = 1000/fps;
var delta;

function tick() {
  requestAnimationFrame(tick);
  now = Date.now();
  delta = now - then;
  if (delta > interval) {
    // 這裏不能簡單then=now,不然還會出現上邊簡單作法的細微時間差問題。例如fps=10,每幀100ms,而如今每16ms(60fps)執行一次draw。16*7=112>100,須要7次才實際繪製一次。這個狀況下,實際10幀須要112*10=1120ms>1000ms才繪製完成。
    then = now - (delta % interval);
    draw(); // ... Code for Drawing the Frame ...
  }
}
tick();
複製代碼

3. transition 與 animate

transition:通常用來作過渡的, 沒時間軸的概念, 經過事件觸發(一次),沒中間狀態(只有開始和結束)
animate:作動效,有時間軸的概念(幀可控),能夠重複觸發和有中間狀態;
過渡的開銷比動效小,前者通常用於交互居多,後者用於活動頁居多;瀏覽器

如何優化動畫

大部分網站性能優化能夠對動畫有必定的優化做用,在這裏不累贅地細講了,詳情請看 -》網站性能優化實戰:juejin.im/post/5b0b7d…性能優化

本文主要講動畫的爲何會出現動畫卡頓問題,針對這種問題該如何解決。

1. 爲何會形成動畫卡頓呢?

Blink 內核早期架構
以 Chrome 瀏覽器內核 Blink 渲染頁面爲例。對早期的 Chrome 瀏覽器而言,每一個頁面 Tab 對應一個獨立的 renderer 進程,Renderer 進程中包含了主線程和合成線程。早期 Chrome 內核架構:


其中,主線程主要負責:

  • Javascript 的計算與執行
  • CSS 樣式計算
  • Layout 計算
  • 將頁面元素繪製成位圖(paint),也就是光柵化(Raster)
  • 將位圖給合成線程

合成線程則主要負責:

  • 經過GPU,將位圖繪製到屏幕上
  • 對可見或即將可見的區域,詢問主線程是否進行位圖更新。
  • 計算頁面的可見區域
  • 當滾動屏幕時,計算出即將可見的區域
  • 當滾動時移動頁面區域

主線程和合成線程的調度不合理會形成渲染出現卡頓等問題。

2. CSS 動畫與 JS 動畫的細微區別

FPS(JS) = Time(主線程) + Time(合成線程)
FPS(CSS) = Time(合成線程)(有時候,例如opacity, transform)

在不頻繁觸發主線程時,css 動畫比 js 動畫節省性能。
若是任何動畫觸發了繪製,佈局,或者二者,那麼「主線程」會來完成該工做。這個對基於 CSS 仍是 JavaScript 實現的動畫都同樣,佈局或者繪製的開銷巨大,讓與之關聯的 CSS 或 JavaScript 執行工做、渲染都變得毫無心義。

3. CSS 動畫卡頓性能優化

例子:

transition:margin 2s;
複製代碼

在使用height,width,margin,padding做爲transition的值時,會形成瀏覽器主線程的工做量較重,例如從margin-left:-20px渲染到margin-left:0,主線程須要計算樣式margin-left:-19px,margin-left:-18px,一直到margin-left:0,並且每一次主線程計算樣式後,合成進程都須要繪製到GPU而後再渲染到屏幕上,先後總共進行20次主線程渲染,20次合成線程渲染,20+20次,總計40次計算。

主線程的渲染流程,能夠參考瀏覽器渲染網頁的流程:

  • 使用 HTML 建立文檔對象模型(DOM)
  • 使用 CSS 建立 CSS 對象模型(CSSOM)
  • 基於 DOM 和 CSSOM 執行腳本(Scripts)
  • 合併 DOM 和 CSSOM 造成渲染樹(Render Tree)
  • 使用渲染樹佈局(Layout)全部元素
  • 渲染(Paint)全部元素

也就是說,主線程每次都須要執行Scripts,Render Tree ,Layout和Paint這四個階段的計算。

transition:transform 2s;
複製代碼

而若是使用transform的話,例如tranform:translate(-20px,0)到transform:translate(0,0),主線程只須要進行一次tranform:translate(-20px,0)到transform:translate(0,0),而後合成線程去一次將-20px轉換到0px,這樣的話,總計1+20計算。

css 動畫卡頓的解決方案:

  1. 在使用css3 transtion作動畫效果時,優先選擇transform,儘可能不要使用height,width,margin和padding。
  2. 選擇器越複雜,瀏覽器計算得越久。最糟狀況下,瀏覽器須要遍歷整個DOM-tree,計算量等於元素總個數乘以選擇器個數。儘可能不要使選擇器太複雜,事先給須要被操做的元素加上類名。
  3. reflow老是牽涉整個文檔流。修改元素css後馬上讀取css計算值,將致使瀏覽器同步reflow,阻塞js線程。
  4. 使用 will-change 通知瀏覽器你打算更改元素的屬性。瀏覽器會在你進行更改以前作最合適的優化。但不要過分使用 will-change,由於這樣作會浪費瀏覽器資源,從而致使更多的性能問題。

相關動畫插件

  • Echarts:很少說
  • Zrender:Echarts 是基於 Zrender 封裝、加工實現的。Zrender 又是對 canvas 的封裝。
  • motaion:螞蟻對外的動畫組件
  • three.js:3D 動畫,基於 webGL 封裝的 API
  • bodymovin:ui導出相應的文件,使用插件實現動畫效果。
  • chart.js:與 Ecahrts 相似的插件

相關參考資料

相關文章
相關標籤/搜索