CSS3 動畫卡頓效能優化解決方案

發表於2018-02-06
 1460000013045038
最近在開發小程式,與vue類似,它們都有生命週期這回事。

onLoad 監聽頁面載入
onReady 監聽頁面初次渲染完成
onShow 監聽頁面顯示

到底是什麼意思?

所以這又觸碰到了我的知識盲區,不過專案在磕磕絆絆中完成的差不多了,但是遇到了CSS3動畫渲染的效能問題,所以我也是被逼的,再回過頭來從瀏覽器渲染網頁的流程出發,去找動畫卡頓的癥結。

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

使用 HTML 建立文件物件模型(DOM)
使用 CSS 建立 CSS 物件模型(CSSOM)
基於 DOM 和 CSSOM 執行指令碼(Scripts)
合併 DOM 和 CSSOM 形成渲染樹(Render Tree)
使用渲染樹佈局(Layout)所有元素
渲染(Paint)所有元素

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

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

如何判斷手機app是native,webview還是hybird?
簡單說下,app中的一大塊是白色的沒有紅線標記出來的,但是上面有按鈕,圖片等時,就是webview,也就是通過一個偽瀏覽器去請求到的資料,斷網時開啟app沒有任何東西顯示在上面

1460000013045039

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端支援性需要格外注意。


補充:為了增強本文的說服力,特地回家做了一個實驗,程式碼如下。

我將主要藉助chrome devtools的performance工具對比二者的效能差異。
先來看margin動畫,動態修改DOM節點的margin-left值從0到500px;。

1460000013045040
1460000013045041
1460000013045042

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

1460000013045043

1460000013045044

 

 

1460000013045045

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

耗時 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。

That’s it !

參考:
http://sy-tang.github.io/2014…
http://jinlong.github.io/2017…
http://blog.csdn.net/yeana1/a…
https://www.jianshu.com/p/b70…
https://developers.google.com…
http://blogs.adobe.com/webpla…

相關文章