1. 程式人生 > >你真的瞭解CSS3硬體加速嗎?

你真的瞭解CSS3硬體加速嗎?

常聽人說:

移動端要想動畫效能流暢,應該使用3d硬體加速

最近深入瞭解了一些瀏覽器核心的細節,感覺這裡面其實有坑啊。。。

事情要從最近看的《WebKit技術內幕》說起,第二章介紹了網頁的結構,其中提到了Webkit硬體加速的方式,會把需要渲染的元素放到特定的『Composited Layer』中,在chrome的控制檯可以這樣開啟:

選擇『Show composited layer borders』以後,就能看到有動畫3d變換的元素會被一個黃色的邊框圈起來,表示放到了一個新的『複合層(composited layer)』中渲染,大概長這個樣子:

藍色的細線是瀏覽器渲染時候的『瓦片』,瀏覽器繪製頁面的時候只會繪製可視區域一定範圍內的瓦片,以節省效能開銷,而黃色的邊框框起來的,就代表了這個元素被放到特殊的複合層中渲染,跟主文件不在一個層中

然後我覺得這個檢視挺有意思的,就拿來看了一下國內某專案,不看不知道,一看被嚇尿:

這個專案什麼時候搞成所有元素都用3d加速了?!

仔細排查了這些被框出來的元素,完全沒有任何需要複合層渲染的跡象,我真是嗶了狗了。。。我開始一個個刪除元素,簡化程式碼,很快就發現,原來罪魁禍首在這裡:

頭部的那個輪播動畫元素的存在居然會導致下面所有相對和絕對定位的元素都被放到複合層中。。。

查了一些 資料

層建立標準

什麼情況下能使元素獲得自己的層?雖然 Chrome 的啟發式方法(heuristic)隨著時間在不斷髮展進步,但是從目前來說,滿足以下任意情況便會建立層:

  • 3D 或透視變換(perspective transform) CSS 屬性
  • 使用加速視訊解碼的 元素
  • 擁有 3D (WebGL) 上下文或加速的 2D 上下文的 元素
  • 混合外掛(如 Flash)
  • 對自己的 opacity 做 CSS 動畫或使用一個動畫 webkit 變換的元素
  • 擁有加速 CSS 過濾器的元素
  • 元素有一個包含複合層的後代節點(換句話說,就是一個元素擁有一個子元素,該子元素在自己的層裡)
  • 元素有一個 z-index 較低且包含一個複合層的兄弟元素(換句話說就是該元素在複合層上面渲染)

主要是最後一條,我覺得它的中文翻譯不是很準確,原文其實是:

Element has a sibling with a lower z-index which has a compositing layer (in other words the it’s rendered on top of a composited layer)

這句話的意思是,如果有一個元素,它的兄弟元素在複合層中渲染,而這個兄弟元素的z-index比較小,那麼這個元素(不管是不是應用了硬體加速樣式)也會被放到複合層中。

最可怕的是,瀏覽器有可能給複合層之後的所有相對或絕對定位的元素都建立一個複合層來渲染,於是就有了上面那個專案截圖的那種效果。之前一直奇怪為什麼這個頁面滾動很卡,明明沒有多少DOM,現在看來問題就在這裡了!

於是乎我寫了一個頁面,讓大家看看這東西到底有多大威力:

我在上面這個頁面中放置了一個h1標題,應用了translate3d動畫,使得它被放到composited layer中渲染,然後在這個元素後面建立了2000個list,每個list中都有一個圖片,一個標題和一個日期顯示,其中圖片和日期顯示是絕對定位,父容器li是相對定位,然後,各位可以按照前述的說明開啟chrome的『show composited layer borders』選項看看這個頁面的內容複合層分佈:

就是這個鳥樣子,很難想象,這樣的頁面滾動起來會卡成什麼樣。我用的是mac機器,快速拖動滾動條chrome已經非常吃力了,然後我寫了一個簡單的滾動條移動操作:

setInterval('document.body.scrollTop++', 0);

然後用timeline抓一下頁面效能:

一次『Composite Layers』的計算居然要 96.206 ms !!這還是在我的mac系統上哦,手機上真的會卡出翔。

我在頁面上放置了一個開關『為動畫元素設定z-index』,這個checkbox點選之後,會用js給那個動畫的h1元素加 position:relative 和 z-index: 1 ,這種做法的原理是人為提升動畫元素的z-index,讓瀏覽器知道這個元素的層排序,就不會很傻逼的把其他z-index比它高的元素也弄到複合層中了,看看這個效果:

僅僅給動畫元素設定一個高一些的z-index,就能解決這種無厘頭增加複合層的問題,略無語。。。搞定之後,再用滾動條移動函式抓一下頁面效能:

完全恢復正常了有木有!

大家可以用支援『硬體加速』的『安卓』手機瀏覽器測試上述頁面,給動畫元素加z-index前後的效能差距非常明顯。

不過也不是所有瀏覽器都有這個問題,我在mac上的Safari、firefox都沒有明顯差異,安卓手機上的QQ瀏覽器好像也正常,獵豹、UC、歐朋、webview等瀏覽器差距明顯,更多測試就靠大家來發現吧。

最後總結一下:

使用3D硬體加速提升動畫效能時,最好給元素增加一個z-index屬性,人為干擾複合層的排序,可以有效減少chrome建立不必要的複合層,提升渲染效能,移動端優化效果尤為明顯。

大家可以現在就排查一下這類問題,尤其是用了輪播、動畫loading的頁面,出現這問題很常見。另外推薦在追查效能問題的時候開啟『show composited layer borders』選項,如果頁面有很多黃色的框肯定是不對的。

最後,再次推薦一下《Webkit技術內幕》這本書。瀏覽器核心之於前端工程師,就如同作業系統之於後端工程師,畢竟是我們程式執行的宿主環境,多瞭解一些,很多問題容易想通。