一、圖層
圖層即層疊上下文,具體概念和應用大家可以看我之前轉自張鑫旭大神博客的《CSS層疊上下文和層疊順序》,這里我們簡單復習一下產生層疊上下文的原因。
1.根層疊上下文
指的是頁面根元素,也就是滾動條的默認的始作俑者<html>元素。這就是為什么,絕對定位元素在left/top等值定位的時候,如果沒有其他定位元素限制,會相對瀏覽器窗口定位的原因。
2.定位元素與傳統層疊上下文
對於包含有position:relative/position:absolute的定位元素,以及FireFox/IE瀏覽器(不包括Chrome等webkit內核瀏覽器)(目前,也就是2016年初是這樣)下含有position:fixed聲明的定位元素,當其z-index值不是auto的時候,會創建層疊上下文。
3.CSS3與新時代的層疊上下文
以下情況會產生新的層疊上下文:
- 根元素(HTML)
- 絕對或相對定位且
z-index值不為auto
- 一個伸縮項目
Flex Item,且z-index值不為auto,即父元素display: flex|inline-flex
- 元素的
opacity屬性值小於 1
- 元素的
transform屬性值不為none
- 元素的
mix-blend-mode屬性值不為normal
- 元素的
filter屬性值不為normal
- 元素的
isolation屬性值為isolate
position: fixed
will-change中指定了上述任意屬性,即便你沒有直接定義這些屬性
- 元素的
-webkit-overflow-scrolling屬性值為touch
二、利用絕對定位+top/left實現動畫

上圖數據中的綠色條紋表示的就是使用 top 和 left 實現動畫時瀏覽器發生的 repaint 操作,從中可以看出動畫幀數遠低於60 幀。
從 chrome 的開發者工具按 ESC 之后選擇 “rendering” 面板,我們可以通過選中“Enable piant flashing”來進一步監測 repaint 操作。開啟該選項后,頁面中的 repaint 區域就會被綠色蒙版高亮顯示出來。重新使用 top 和 left 的示例演示的話,你會發現包裹球的那塊區域會一直閃爍綠色的蒙版。

按照常理來說,改變元素位置會產生重排,為什么上面圖中顯示的全是重繪呢?原因是絕對定位會建立一個新的圖層,而此圖層上只有當前一個元素,多以只會重繪,而不會重排。這也告訴我們,在同一層中,元素數量少的情況下,重排性能對更好,速度會更快。
三、transform2D實現動畫
下圖是使用CSS transform 檢測到的數據:

如你所見,動畫演示期間並沒有過多的 repaint 操作。
那么,為什么 transform 沒有觸發 repaint 呢?簡而言之,transform 動畫由GPU控制,支持硬件加速,並不需要軟件方面的渲染。
四、硬件加速原理
瀏覽器接收到頁面文檔后,會將文檔中的標記語言解析為DOM樹。DOM樹和CSS結合后形成瀏覽器構建頁面的渲染樹。渲染樹中包含了大量的渲染元素,每一個渲染元素會被分到一個圖層中,每個圖層又會被加載到GPU形成渲染紋理,而圖層在GPU中transform 是不會觸發 repaint 的,最終這些使用 transform 的圖層都會由獨立的合成器進程進行處理。
在我們的示例中,CSS transform 創建了一個新的復合圖層,可以被GPU直接用來執行 transform 操作。在chrome開發者工具中開啟“show layer borders”選項后,每個復合圖層就會顯示一條黃色的邊界:
示例中的球就處於一個獨立的復合圖層,移動時的變化也是獨立的:

此時,你也許會問:瀏覽器什么時候會創建一個獨立的復合圖層呢?事實上一般是在以下幾種情況下:
- 3D 或者 CSS transform
<video>和<canvas>標簽- CSS filters
- 元素覆蓋時,比如使用了
z-index屬性
等一下,上面的示例使用的是 2D transition 而不是 3D 的 transforms 啊?這個說法沒錯,所以在timeline中我們可以看到:動畫開始和結束的時候發生了兩次 repaint 操作。

3D 和 2D transform 的區別就在於,瀏覽器在頁面渲染前為3D動畫創建獨立的復合圖層,而在運行期間為2D動畫創建。動畫開始時,生成新的復合圖層並加載為GPU的紋理用於初始化 repaint。然后由GPU的復合器操縱整個動畫的執行。最后當動畫結束時,再次執行 repaint 操作刪除復合圖層。
五、強制GPU渲染
並不是所有的CSS屬性都能觸發GPU的硬件加速(圖層在GPU中屬性改變不會觸發 repaint ),實際上只有少數屬性可以,比如下面的這些:
transformopacityfilter
為了避免 2D transform 動畫在開始和結束時發生的 repaint 操作,我們可以硬編碼一些樣式來解決這個問題:
.example1 { transform: translateZ(0); } .example2 { transform: rotateZ(360deg); }
這段代碼的作用就是讓瀏覽器執行 3D transform。瀏覽器通過該樣式創建了一個獨立圖層,圖層中的動畫則有GPU進行預處理並且觸發了硬件加速。
如果某一個元素的背后是一個復雜元素,那么該元素的 repaint 操作就會耗費大量的資源,此時也可以使用上面的技巧來減少性能開銷。
六、使用硬件加速的問題
使用硬件加速並不是十全十美的事情,比如:
- 內存。如果GPU加載了大量的紋理,那么很容易就會發生內容問題,這一點在移動端瀏覽器上尤為明顯,所以,一定要牢記不要讓頁面的每個元素都使用硬件加速。
- 使用GPU渲染會影響字體的抗鋸齒效果。這是因為GPU和CPU具有不同的渲染機制。即使最終硬件加速停止了,文本還是會在動畫期間顯示得很模糊。
七、總結
感覺這里面邏輯很亂,在這里整理一下。首先transform和絕對定位都會產生新的圖層,所以都不存在重排,圖層在GPU中transform又不會引起重繪,這就是硬件加速的原理。另外,transform3D和2D的區別在於3D渲染前便會產生新的圖層,而2D是在運行時產生圖層,運行結束時刪除圖層。
注:本文在整理了南北在W3C上寫的《CSS動畫之硬件加速》中的知識點,並加入了自己的理解和總結
