前言
近些年,我們總是聽到硬件加速,以及它如何幫助我們提升網頁的動畫性能,讓網頁動畫變得更好,在移動端更流暢。但是我想一大部分經驗少的工程師是不知道硬件加速是如何工作的以及我們如何使用它來幫助我們讓動畫變得更流暢。
CSS3 硬件加速又叫做 GPU 加速,是利用 GPU 進行渲染,減少 CPU 操作的一種優化方案。由於 GPU 中的 transform3d 等 CSS 屬性不會觸發 repaint,所以能大大提高網頁的性能。
GPU(圖像處理器):
GPU 硬件加速是指應用 GPU 的圖形性能對瀏覽器中的一些圖形操作交給 GPU 來完成,因為 GPU 是專門為處理圖形而設計,所以它在速度和能耗上更有效率
案例
左邊元素的動畫通過 left/top 操作位置實現,右邊元素的動畫通過 transform: translate
實現,你可以打開 chrome 的 “Paint flashing” 查看,綠色部分是正在 repaint 的內容。
從 demo 中可以看到左邊的圖形在運動時外層有一圈綠色的邊框,表示元素不停地 repaint,並且可以看到其運動過程中有丟幀現象,具體表現為運動不連貫,有輕微閃動。
那么transform
是如何讓動畫不會導致重繪的呢?最直接的答案就是transform
會直接使用硬件加速,在GPU
中運行,繞開了軟件渲染。
硬件加速如何工作的
之前學習 flash 的時候,就知道動畫是由一幀一幀的圖片組成,在瀏覽器中也是如此。我們首先看一下,瀏覽器每一幀都做了什么。
-
JavaScript:JavaScript 實現動畫效果,DOM 元素操作等。
-
Style(計算樣式):確定每個 DOM 元素應該應用什么 CSS 規則。
-
Layout(布局):計算每個 DOM 元素在最終屏幕上顯示的大小和位置。由於 web 頁面的元素布局是相對的,所以其中任意一個元素的位置發生變化,都會聯動的引起其他元素發生變化,這個過程叫 reflow。
-
Paint(繪制):在多個層上繪制 DOM 元素的的文字、顏色、圖像、邊框和陰影等。
-
Composite(渲染層合並):按照合理的順序合並圖層然后顯示到屏幕上。
動畫與圖層
瀏覽器在獲取 render tree(詳細知識可以查看深入了解瀏覽器重排和重繪)后,渲染樹中包含了大量的渲染元素,每一個渲染元素會被分到一個圖層中,每個圖層又會被加載到 GPU 形成渲染紋理。這里的秘訣就在於通過transform
的層會使用GPU
渲染,因此不需要重繪,這一點非常類似 3D 繪圖功能,最終這些使用 transform 的圖層都會由獨立的合成器進程進行處理。
過程如下:
render tree -> 渲染元素 -> 圖層 -> GPU 渲染 -> 瀏覽器復合圖層 -> 生成最終的屏幕圖像。
注意:
chrome devtools 中可以開啟 Rendering 中的 Layer borders 查看圖層紋理。
其中黃色邊框表示該元素有 3d 變換,表示放到一個新的復合層(composited layer)中渲染,藍色柵格表示正常的 render layer。
在文章開始給出的例子中,CSS
的transform
在GPU
直接創建一個新的層。我們也可以開啟 Layer borders,(這個選項可以幫助我們查看哪些是單獨的層,開啟這個選項以后單獨的層會具有一個橙色的邊框。)可以觀察到,使用 transform: translate
動畫的元素,外圍有一個黃色的邊框,可知其為復合層。
在 GPU 渲染的過程中,一些元素會因為符合了某些規則,而被提升為獨立的層(黃色邊框部分),一旦獨立出來,就不會影響其它 DOM 的布局,所以我們可以利用這些規則,將經常變換的 DOM 主動提升到獨立的層,那么在瀏覽器的一幀運行中,就可以減少 Layout 和 Paint 的時間了。
創建獨立圖層
哪些規則能讓瀏覽器主動幫我們創建獨立的層呢?
-
3D 或者透視變換(perspective,transform) 的 CSS 屬性。
-
使用加速視頻解碼的 video 元素。
-
擁有 3D(WebGL) 上下文或者加速 2D 上下文的 canvas 元素。
-
混合插件(Flash)。
-
對自己的 opacity 做 CSS 動畫或使用一個動畫 webkit 變換的元素。
-
擁有加速 CSS 過濾器的元素。
-
元素有一個包含復合層的后代節點(換句話說,就是一個元素擁有一個子元素,該子元素在自己的層里)。
-
元素有一個兄弟元素在復合圖層渲染,並且該兄弟元素的 z-index 較小,那這個元素也會被應用到復合圖層。
開啟 GPU 加速
CSS的animation、tranform、transition並不會自動開啟GPU加速,而是通過瀏覽器的緩慢的軟件渲染引擎來實現執行,那么我們怎么才能實現GPU加速呢,很多瀏覽器提供了某些觸發該模式的規則。
比如使用 translate3d() rotate3d() scale3d() 這幾個方法,我們就可以使用GPU加速了。
如下幾個css屬性可以觸發硬件加速:
-
transform( translate3d、translateZ(0)等)
-
opacity
-
filter(濾鏡:drop-shadow()、opacity(),函數與已有的
box-shadow、opacity
屬性很相似;不同之處在於,通過濾鏡,一些瀏覽器為了更好的性能會提供硬件加速) -
will-change:哪一個屬性即將發生變化,進而進行優化。
因此為了頁面更加流暢,高性能的動畫,我們可以使用GPU
來處理。
如果有一些元素不需要用到上述屬性,但是需要觸發硬件加速效果,例如:某些情況下,我們並不想要對元素應用3D變換的效果,卻還想要實現GPU加速,可以使用一些小技巧來誘導瀏覽器開啟硬件加速。
transform: translateZ(0)
這個聲明就是可以觸發桌面端和移動端的GPU加速,這是一個非常有效的方式(包含所有的瀏覽器前綴):
.element {
-webkit-transform: translateZ(0);
-moz-transform: translateZ(0);
-ms-transform: translateZ(0);
-o-transform: translateZ(0);
transform: translateZ(0);
/**或者**/
transform: rotateZ(360deg);
transform: translate3d(0, 0, 0);
}
使用硬件加速需要注意的地方
Memory
大部分重要的問題都是關於內存。GPU
處理過多的內容會導致內存問題。這在移動端和移動端瀏覽器會導致崩潰。因此,通常不會對所有的元素使用硬件加速。(過多地開啟硬件加速可能會耗費較多的內存,因此什么時候開啟硬件加速,給多少元素開啟硬件加速,需要用測試結果說話。)
Font rendering
在GPU
渲染字體會導致抗鋸齒無效。這是因為GPU
和CPU
的算法不同。因此如果你不在動畫結束的時候關閉硬件加速,會產生字體模糊。
The Near Future
有必要使用transform hack
的地方是提高性能。瀏覽器自身也提供了優化的功能,這也就是will-change
屬性。這個功能允許你告訴瀏覽器這個屬性會發生變化,因此瀏覽器會在開始之前對其進行優化。這里有一個例子:
.example { will-change: transform; }
遺憾的是,並不是所有瀏覽器都支持這個功能。
GPU優點
-
GPU
渲染可以提高動畫性能 -
GPU
渲染會提高動畫的渲染幀數
參考
Web 性能優化-CSS3 硬件加速(GPU 加速)(推薦)
在 CSS 動畫中使用硬件加速(翻譯)(結合推薦)
https://www.shuzhiduo.com/R/KE5Q009kJL/ (很多文章關於硬件加速)
https://www.nowcoder.com/questionTerminal/0260851028b740a1a58370495fe1077d(牛客面試題)