從瀏覽器渲染層面解析 css3 動效優化原理
引言—
在 h5 開發中,我們經常會需要實現一些動效來讓頁面視覺效果更好,談及動效便不可避免地會想到動效性能優化這個話題:
-
減少頁面 DOM 操作,可以使用 CSS 實現的動效不多出一行 js 代碼
-
使用絕對定位脫離讓 DOM 脫離文檔流,減少頁面的重排 (relayout)
-
使用 CSS3 3D 屬性開啓硬件加速
那麼,CSS3 與動效優化有什麼關係呢,本文將從瀏覽器渲染層面講述 CSS3 的動效優化原理
瀏覽器頁面展示過程—
首頁,我們需要了解一下瀏覽器的頁面展示過程:
-
Javascript:主要負責業務交互邏輯。
-
Style: 根據 CSS 選擇器,對每個 DOM 元素匹配對應的 CSS 樣式。
-
Layout: 具體計算 DOM 元素顯示在屏幕上的大小及位置。
-
Paint: 實現一個 DOM 元素的可視效果 (顏色、邊框、陰影等),一般來說由多個渲染層完成。
-
Composite: 當每個層繪製完成後,瀏覽器會將所有層按照合理順序合併爲一個圖層,顯示到屏幕。本文我們將重點關注
Composite
過程。
瀏覽器渲染原理—
在討論 Composite
之前,我們還需要了解一下瀏覽器渲染原理
從該圖中,我們可以發現:
-
DOM 元素
與Layout Object
存在一一對應的關係 -
一般來說,擁有相同座標空間的
Layout Object
屬於同一個Paint Layer (渲染層)
,通過position、opacity、filter
等 CSS 屬性可以創建新的 Paint Layer -
某些特殊的
Paint Layer
會被認爲是Composite Layer (合成層/複合層)
,Composite Layer 擁有單獨的Graphics Layer (圖形層)
,而那些非 Composite Layer 的 Paint Layer,會與擁有 Graphics Layer 的父層共用一個
Graphics Layer
我們日常生活中所看到屏幕可視效果可以理解爲:由多個位圖通過 GPU
合成渲染到屏幕上,而位圖的最小單位是像素。如下圖:
那麼位圖是怎麼獲得的呢,Graphics Layer
便起到了關鍵作用, 每個 Graphics Layer
都有一個 Graphics Context
, 位圖是存儲在共享內存中,Graphics Context
會負責將位圖作爲紋理
上傳到GPU
中,再由 GPU 進行合成渲染。如下圖:
CSS 在瀏覽器渲染層面承擔了怎樣的角色
大多數人對於 CSS3 的第一印象,就是可以通過 3D(如 transform) 屬性來開啓硬件加速,許多同學在重構某一個項目時,考慮到動畫性能問題,都會傾向:
- 將 2Dtransform 改爲 3Dtransform 2. 將 left (top、bottom、right) 的移動改爲 3Dtransform
但開啓硬件加速的底層原理
其實就在於將 Paint Layer 提升到了 Composite Layer
以下的幾種方式都用相同的作用:
-
3D 屬性開啓硬件加速 (3d-transform)
-
will-change: (opacity、transform、top、left、bottom、right)
-
使用 fixed 或 sticky 定位
-
對 opacity、transform、filter 應用了 animation(actived) or transition(actived),注意這裏的 animation 及 transition 需要是處於
激活狀態
纔行
我們來寫兩段 demo
代碼,帶大傢俱體分析一下實際情況
demo1. 3D 屬性開啓硬件加速 (3d-transform)
.composited{
width: 200px;
height: 200px;
background: red;
transform: translateZ(0)
}
</style>
<div class="composited">
composited - 3dtransform
</div>
demo2. 對 opacity、transform、filter 應用 animation(actived) or transition(actived)
<style>
@keyframes move{
0%{
top: 0;
}
50%{
top: 600px;
}
100%{
top: 0;
}
}
@keyframes opacity{
0%{
opacity: 0;
}
50%{
opacity: 1;
}
100%{
opacity: 0;
}
}
#composited{
width: 200px;
height: 200px;
background: red;
position: absolute;
left: 0;
top: 0;
}
.both{
animation: move 2s infinite, opacity 2s infinite;
}
.move{
animation: move 2s infinite;
}
</style>
<div id="composited" class="both">
composited - animation
</div>
<script>
setTimeout(function(){
const dom = document.getElementById('composited')
dom.className = 'move'
},5000)
</script>
這裏我們定義了兩個keyframes(move、opacity)
,還有兩個class(both、move)
,起初 #composited
的 className = 'both'
,5 秒延時器後,className = 'move'
,我們來看看瀏覽器的實際變化。
起初:#composited 創建了一個複合層,並且運動時 fps 沒有波動,性能很穩定
5 秒後:複合層消失,運動時 fps 會發生抖動,性能開始變得不再穩定
如何查看複合層及 fps
在瀏覽器的 Dev Tools
中選擇 More tools
,並勾選 Rendering
中的 FPS meter
動畫性能最優化
之前,我們提到了頁面呈現出來所經歷的渲染流水線,其實從性能方面考慮,最理想的渲染流水線是沒有佈局和繪製環節的
,爲了實現上述效果,就需要只使用那些僅觸發 Composite
的屬性。
目前,只有兩個屬性是滿足這個條件的:transforms
和 opacity
(僅部分瀏覽器支持)。
相關信息可查看:css Triggers[1]
總結
提升爲合成層簡單說來有以下幾點好處:
-
合成層的位圖,會交由 GPU 合成,比 CPU 處理要快
-
當需要 repaint 時,只需要 repaint 本身,不會影響到其他的層
-
對於 transform 和 opacity 效果,部分瀏覽器不會觸發 Layout 和 Paint, 相關信息可查看:css Triggers[1]
缺點:
-
創建一個新的合成層並不是免費的,它得消耗額外的內存和管理資源。
-
紋理上傳後會交由 GPU 處理,因此我們還需要考慮 CPU 和 GPU 之間的帶寬問題、以及有多大內存供 GPU 處理這些紋理的問題
大多數人都很喜歡使用 3D 屬性 translateZ(0) 來進行所謂的硬件加速,以提升性能。但我們還需要切實的去分析頁面的實際性能表現,不斷的改進測試,這樣纔是正確的性能優化途徑。
參考資料
[1]
css Triggers: https://csstriggers.com/?spm=taofed.bloginfo.blog.36.20e75ac8xZGHBo
[2]
無線性能優化:Composite - 淘系前端團隊: https://fed.taobao.org/blog/taofed/do71ct/performance-composite/?spm=taofed.blogs.header.7.63e65ac801qdAI
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/sVGEQjcfTsljMup2OBoAzQ