CSS動畫的效能分析和瀏覽器GPU加速

網易雲社群發表於2018-10-31

此文已由作者袁申授權網易雲社群釋出。

歡迎訪問網易雲社群,瞭解更多網易技術產品運營經驗。

有數的資料大屏可以在一塊螢幕上展示若干張不同的圖表,以炫酷的方式展示各種業務資料。其中有些圖表使用CSS實現了餅圖輪播、地圖示記點閃爍等動畫,然而在一張大屏上同時顯示了許多張圖表時,持續的動畫效果有時會出現掉幀、卡頓的情況,需要對動畫效能進行優化。本文簡單介紹了chrome瀏覽器效能分析工具和CSS動畫使用GPU加速進行效能優化的解決方案。

瀏覽器渲染流程

這是瀏覽器渲染引擎的處理過程:


Alt pic

接收到文件後,渲染引擎會對HTML文件進行解析生成DOM樹、對CSS檔案進行解析生成CSSOM樹;同時執行頁面中的JavaScript程式碼;最終根據DOM樹和CSSOM樹,計算樣式(Caluclate Style)生成渲染樹,渲染樹中,只會包含即將顯示在頁面中的元素及其樣式資訊(如head元素、display為hidden的元素就不會包含在渲染樹中);根據渲染樹需要進行佈局(layout)來計算每個元素在頁面上的位置;

接下來渲染引擎開始進行繪製(paint),這一步分為若干階段:根據渲染樹將每層(layer)的各個元素繪製,然後將繪製出的若干連續影像進行柵格化(Rasterization),最後將柵格化後的影像合成(composite)為要顯示在螢幕上的影像。對每一層的繪製是由瀏覽器來完成的;最後的合成是由GPU來完成;而柵格化過程取決於瀏覽器的設定,chrome預設開啟GPU柵格化,否則由CPU進行。

當首次將DOM樹構建完成後,每次頁面發生改變時進行進行的主要流程為:

Alt pic


其中CSS動畫不會呼叫JavaScript,我們知道,在渲染中主要消耗時間的是Layout/Reflow和Paint/Repaint的過程,因此要儘量避免和減少這兩個階段的時間。

影響CSS動畫效能的因素

這是一個CSS動畫,控制一個方塊的top、left屬性實現平移。使用Chrome提供的瀏覽器效能分析工具分析動畫的效能,開啟瀏覽器開發者工具後,在標籤中選擇performance開啟效能分析皮膚。在效能分析皮膚中對當前頁面進行錄製,錄製結束後分析結果中可以檢視頁面在這段時間內的FPS、CPU佔用等情況,在main中包含了瀏覽器的渲染流程,包括Scripting、Rendering、Painting等。

Alt pic

執行這個CSS動畫時,不涉及JavaScript的呼叫;紫色部分是render,依次分別為Recaculate Style、Layout和Update Layer Tree;綠色部分為Painting,依次分別為Paint和Composite Layers。

在下面的皮膚中也可以檢視當前時間段內各個階段執行時間

Alt pic

Alt pic

more tools中的rendering也包含若干檢視渲染有關的選項:

Alt pic

勾選paint flashing,頁面上會以綠色方塊顯示需要重繪的區域,當前小方塊進行了平移,因此需要重繪;勾選layer borders,會以黃色方框顯示頁面的分層情況,此時頁面只有一個層,藍色的線顯示了tile的劃分,它是一個layer中的分塊;FPS meter可以顯示頁面的FPS、是否使用GPU進行柵格化過程和GPU視訊記憶體使用情況,由於預設開啟了GPU柵格化,GPU Raster顯示為on:

Alt pic

more tools中的layers可以檢視頁面分層,以及每層的詳細資訊等:

Alt pic

通過對這些資料我們可以對頁面載入的效能瓶頸進行分析和針對性的優化。

使用GPU加速

瀏覽器的GPU加速功能是將需要進行動畫的元素提升到一個獨立的層(layer),這樣就可以避免瀏覽器進行重新佈局(Reflow)和繪製(Repaint),將原先的瀏覽器使用CPU繪製點陣圖來實現的動畫效果轉為讓GPU使用圖層合成(composite)來實現,如果兩張圖層內部沒有發生改變,瀏覽器就不再進行佈局和繪製,直接使用GPU的快取來繪製每個圖層,GPU只負責將各個圖層合成來實現動畫,這就可以充分利用GPU的資源和優勢,減輕CPU的負載,可以使動畫更流暢。通過改變兩張圖片之間的相對位置代替繪製一張圖片的每一幀來實現動畫,雖然視覺效果相同,但省去了許多繪製的時間。

為了讓瀏覽器將動畫元素提升到一個獨立的層,可以使用transform和opacity屬性來實現動畫,當設定了這兩個屬性之一時,瀏覽器會自動進行這一優化操作(透明度的變化可以通過GPU改變a通道來實現,不需要瀏覽器進行重繪)。對於上面的動畫,可以改變transform來代替改變left和top屬性:

這個動畫進行分析,可以看到使用transform後瀏覽器為小方塊單獨設定了一個層,並且不再觸發瀏覽器更新樣式:

Alt pic

Alt pic

如果動畫並不需要對transform和opacity屬性做出改變,可以使用其他的方法強制瀏覽器為這些元素建立單獨的層,比如設定一個沒有效果的樣式:transform:translateZ(0);這不會對元素的實際樣式做出改變。但這是一種hack,規範的做法是使用will-change屬性,設定它的值為需要做變換的屬性,如will-change: left;瀏覽器就會知道left這個屬性會發生變化,因此會開啟硬體加速優化效能。

這是使用will-change屬性的平移動畫,同樣也為小方塊設定了單獨的圖層。

避免過度繪製

既然設定了will-change屬性可以開啟GPU加速,那麼:

* {  will-change: all;}複製程式碼

看起來好像是一勞永逸的方法,但其實這反而會降低頁面的效能,雖然硬體加速可以提高GPU的使用,但從layers中的資訊可以看出,每個層都需要消耗一定的記憶體,過多的記憶體佔用也會造成效能的下降;過多的層傳輸到GPU的過程也會消耗一定的時間,此外也造成合成階段的時間佔用較長,因此並不是獨立的層越多越好。最好的做法是對那些可能動畫的元素設定屬性,並在動畫結束後就移除這個屬性。

SVG圖表動畫效能的優化

根據以上的分析,總結GPU實現動畫的優缺點:

優點:

  • 利用了GPU合成圖層實現動畫,可以做到動畫平滑、流暢

  • 動畫合成工作在GPU執行緒,不會被CPU的js執行阻塞

缺點:

  • 繪圖層必須傳輸到GPU,當圖層較多時傳輸過程可能會導致渲染緩慢

  • 每個複合層都需要消耗額外的記憶體,過多的記憶體可能導致瀏覽器的崩潰

  • 複合層合成需要更多的時間

對於一般的HTML的元素,遵循上述的方法就可以了,但有數大屏中的圖表是使用SVG元素來繪製的,由於並不是標準的DOM元素,Chrome並不能支援SVG元素的硬體加速,即使設定了transform、will-change等屬性,單個的SVG元素也不能作為單獨的層進行繪製。

從這個使用transform實現的SVG動畫可以看到即使使用了transform,動畫部分的頁面仍然需要重繪。

Alt pic

對於下面這個有多個圖表的頁面,其中只有一個圖表有動畫,在動畫過程中,如果只有一個圖層,那麼需要將整個圖層進行重繪,雖然現在Chrome已經可以智慧選擇最小的重繪區域進行增量繪製,但當圖層較大時這樣的判斷也會造成一定的開銷。因此可以考慮將有動畫的圖示單獨放在一個圖層中,其他沒有動畫的圖表和頁面是的其他元素仍然和背景在一個圖層中:

Alt pic

分別對分層的和未分層的頁面進行5次效能測試,將分析結果進行對比:

Alt pic

可以看出,分層後每幀動畫繪製的總時間有明顯下降,主要體現在繪製(painting)過程中:其中paint時間的減少應該是因為每次重繪的圖層面積只有圖表區域,因此需要更少的時間來判斷需要更新紋理的區域;composite layers時間的減少,應該主要是由於需要繪製的圖層的減小,導致紋理上傳GPU和呼叫OpenGL繪製介面的時間減少了;而rendering時間的增加原因還需要進一步的調研。


在實際使用中,可以在某張圖表需要動畫時,設定SVG標籤的will-change屬性,將SVG元素提升到獨立的層,來減少動畫繪製時間;而圖表沒有動畫時則不需要特別處理,避免過度繪製造成的記憶體佔用增加和圖層傳輸、合成時間的增加。

參考



免費體驗雲安全(易盾)內容安全、驗證碼等服務

更多網易技術、產品、運營經驗分享請點選




相關文章:
【推薦】 關於資料庫查詢業務的幾點思考


相關文章