CSS3 動畫卡頓性能優化解決方案--摘抄

最近在開發小程序,與vue相似,它們都有生命週期這回事。css

  • onLoad 監聽頁面加載html

  • onReady 監聽頁面初次渲染完成前端

  • onShow 監聽頁面顯示vue

究竟是什麼意思?android

因此這又觸碰到了個人知識盲區,不過項目在磕磕絆絆中完成的差很少了,可是遇到了CSS3動畫渲染的性能問題,因此我也是被逼的,再回過頭來從瀏覽器渲染網頁的流程出發,去找動畫卡頓的癥結。ios

瀏覽器渲染網頁的流程以下:css3

  • 使用 HTML 建立文檔對象模型(DOM)web

  • 使用 CSS 建立 CSS 對象模型(CSSOM)chrome

  • 基於 DOM 和 CSSOM 執行腳本(Scripts)json

  • 合併 DOM 和 CSSOM 造成渲染樹(Render Tree)

  • 使用渲染樹佈局(Layout)全部元素

  • 渲染(Paint)全部元素

能夠結合Alon的這篇前端性能優化和安卓開發者選項的顯示頁面佈局。

安卓開發者選項的顯示頁面佈局

如何判斷手機app是native,webview仍是hybird?

簡單說下,app中的一大塊是白色的沒有紅線標記出來的,可是上面有按鈕,圖片等時,就是webview,也就是經過一個僞瀏覽器去請求到的數據,斷網時打開app沒有任何東西顯示在上面

onLoad 監聽頁面加載

在渲染完界面以後,也就是經過.json中的配置項生成native界面後,開始渲染webview的部分,一個頁面只會調用一次。

onReady 監聽頁面初次渲染完成

一個頁面只會調用一次,表明頁面已經準備穩當,能夠和視圖層進行交互。

onShow 監聽頁面顯示

每次打開頁面都會去調用其中的函數。

咱們的動畫應該放在哪裏?

應該放在onShow裏,由於這樣我每次打開都能看到動畫。

爲何會卡頓?

有一個前提必需要提,前端開發者們都知道,瀏覽器是單線程運行的。可是咱們要明確如下幾個概念:單線程,主線程和合成線程。

雖說瀏覽器執行js是單線程執行(注意,是執行,並非說瀏覽器只有1個線程,而是運行時,runing),但實際上瀏覽器的2個重要的執行線程,這 2 個線程協同工做來渲染一個網頁:主線程和合成線程。

通常狀況下,主線程負責:運行 JavaScript;計算 HTML 元素的 CSS 樣式;頁面的佈局;將元素繪製到一個或多個位圖中;將這些位圖交給合成線程。

相應地,合成線程負責:經過 GPU 將位圖繪製到屏幕上;通知主線程更新頁面中可見或即將變成可見的部分的位圖;計算出頁面中哪部分是可見的;計算出當你在滾動頁面時哪部分是即將變成可見的;當你滾動頁面時將相應位置的元素移動到可視區域。

那麼爲何會形成動畫卡頓呢?

緣由就是主線程和合成線程的調度不合理。

下面來詳細說一下調度不合理的緣由。

在使用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這四個階段的計算。

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

可能會有人說,這才提高了19次,有什麼好性能提高的?

假設一次10ms。

那麼就減小了約190ms的耗時。

會有人說,辣雞,才190ms,無所謂。

那麼若是margin-left是從-200px到0呢,一次10ms,10ms*199≈2s。

還會有人說,辣雞,也就2s,無所謂。

你忘了單線程這回事了嗎?

若是網頁有3個動畫,3*2s=6s,就是6s的性能提高。

因爲數據是猜想的,因此暫時不考慮其真實性,文章後面我使用chrome devtools的performance作了一個實驗。

要知道,在"客戶至上"的今天,好的用戶體驗是全部產品的必須遵照的一條規則,不管是對於開發者仍是產品經理,追求極致的性能都是咱們打造一個好的產品所必備的品質。

可能看了個人略不專業的分析後,你們對主線程,合成線程以及它們在2種性能不一樣動畫方案上的工做流程還不是很瞭解,能夠去看一篇翻譯過來的博客(英文原版連接已經失效了):深刻瀏覽器理解CSS animations 和 transitions的性能問題

這篇文章完美講述了瀏覽器主線程和合成線程的區別,而且舉了一個高度從100px變化到200px的2種動畫方案的對比,對主線程和合成線程的整個工做流程作了很詳盡的講解,真心建議認真閱讀一遍。

回過頭來總結下,css3動畫卡頓的解決方案:

在使用css3 transtion作動畫效果時,優先選擇transform,儘可能不要使用height,width,margin和padding。

transform爲咱們提供了豐富的api,例如scale,translate,rotate等等,可是在使用時須要考慮兼容性。但其實對於大多數css3來講,mobile端支持性較好,desktop端支持性須要格外注意。

補充:爲了加強本文的說服力,特意回家作了一個實驗,代碼以下。

  1. <!DOCTYPE html>

  2. <html>

  3. <head>

  4.  <meta charset="utf-8" />

  5.  <meta http-equiv="X-UA-Compatible" content="IE=edge">

  6.  <title>Page Title</title>

  7.  <meta name="viewport" content="width=device-width, initial-scale=1">

  8.  <style>

  9.    .margin-transition{

  10.      /* margin-left: 0; */

  11.      background: rgba(0,0,255,0.3);

  12.      transition: margin-left 1s;

  13.    }

  14.    .transform-transition{

  15.      /* transform: translate(0,0); */

  16.      background: rgba(0,255,0,0.3);

  17.      transition: transform 1s;

  18.    }

  19.    .common{

  20.      height: 300px;

  21.      width: 300px;

  22.    }

  23.  </style>

  24. </head>

  25. <body>

  26.  <div class="margin-transition common" id="marginTransition">

  27.    <p>transition:margin-left 1s</p>

  28.  </div>

  29.  <div class="transform-transition common" id="transformTransition">

  30.      <p>transition:tranform 1s</p>

  31.  </div>

  32.  <button id="control">見證奇蹟</button>

  33.  <script>

  34.      var btn = document.getElementById('control');

  35.      var marginTransition = document.getElementById('marginTransition');

  36.      var transformTransition = document.getElementById('transformTransition');

  37.      btn.addEventListener("click",function(){

  38.        console.log(marginTransition.style,transformTransition.style)

  39.        marginTransition.style.marginLeft = "500px";

  40.        transformTransition.style.transform = "translate(500px,0)"

  41.      })

  42.  </script>  

  43. </body>

  44. </html>

我將主要藉助chrome devtools的performance工具對比兩者的性能差別。

先來看margin動畫,動態修改DOM節點的margin-left值從0到500px;。

  1. transition: margin-left 1s;

再來看下transform動畫,動態修改DOM節點的transform值從translate(0,0)到translate(500px,0)。

  1. transition: transform 1s;

可能圖片不是很好地能說明性能差別,那麼咱們來列一張耗時對比表,方便咱們計算。

耗時 margin transform
Summery 3518ms 2286ms
Scripting 1.8ms 2.9ms
Rendering 22.5ms 6.9ms
Painting 9.7ms 1.6ms
Other 39.3ms 25.2ms
Idle( browser is waiting on the CPU or GPU to do some processing) 3444.4ms 2249.8ms
GPU使用率 4.1MB 1.7MB

經過上表咱們能夠計算出明margin,transform與transition組合實現CSS3動畫效果時的性能差別參數。

關鍵性能參數 margin transform
實際動畫耗時(總時間 減去 空閒時間) 73.6ms 36.2ms

計算得出,transform動畫耗時約等於margin動畫耗時的0.49倍,性能優化50%。

因爲我對Other的所作的具體事情不是很清楚,因此這裏的實際動畫時間也有可能還要減掉Other中的時間,下表是咱們減掉後的數據。

關鍵性能參數 margin transform
實際動畫耗時(總時間 減去 其餘時間和空閒時間) 34.3ms 11ms

計算得出,transform動畫耗時約等於margin動畫耗時的0.32倍,性能優化接近70%。

也就是說,不管咱們減去仍是不減去Other的時間,咱們採用transform實現動畫的方式都比margin動畫快。

不精確的得出一個小結論:transform比margin性能好50%~70%

雖然會有50%~70%的性能提高,可是須要注意硬件差別,硬件好的狀況下可能不能發現卡頓或者其餘的一些性能上的問題。

例如在開發小程序的過程當中,模擬器是位於desktop端的,所以它的硬件性能性能更好,例如CPU,GPU。可是一旦在mobile端運行,例如ios或者android上運行時,就可能會出現性能問題,這就是由於移動端的硬件條件遜於PC端致使的。

因此說,性能問題是一直存在的,只不過硬件差別會致使性能影響的程度不一樣。

因此咱們再次回過頭來,總結出css3動畫卡頓的解決方案:

在使用css3 transtion作動畫效果時,優先選擇transform,儘可能不要使用height,width,margin和padding。

相關文章
相關標籤/搜索