0
雷鋒網(wǎng)按:本文作者 被單,原載于 凹凸實(shí)驗(yàn)室,雷鋒網(wǎng)獲授權(quán)轉(zhuǎn)載。
眾所周知,網(wǎng)頁(yè)不僅應(yīng)該被快速加載,同時(shí)還應(yīng)該流暢運(yùn)行,比如快速響應(yīng)的交互,如絲般順滑的動(dòng)畫(huà)……
首先我們要了解什么是 16ms 優(yōu)化
大多數(shù)設(shè)備的刷新頻率是 60 次/秒,(1000/60 = 16.6ms)也就說(shuō)是瀏覽器對(duì)每一幀畫(huà)面的渲染工作要在 16ms 內(nèi)完成,超出這個(gè)時(shí)間,頁(yè)面的渲染就會(huì)出現(xiàn)卡頓現(xiàn)象,影響用戶(hù)體驗(yàn)。
瀏覽器在一幀里面,會(huì)依次執(zhí)行以下這些動(dòng)作。減少或者避免 layout,paint 可以讓頁(yè)面不卡頓,動(dòng)畫(huà)效果更加流暢。
1. JavaScript:JavaScript 實(shí)現(xiàn)動(dòng)畫(huà)效果,DOM 元素操作等。
2. Style(計(jì)算樣式):確定每個(gè) DOM 元素應(yīng)該應(yīng)用什么 CSS 規(guī)則。
3. Layout(布局):計(jì)算每個(gè) DOM 元素在最終屏幕上顯示的大小和位置。由于 web 頁(yè)面的元素布局是相對(duì)的,所以其中任意一個(gè)元素的位置發(fā)生變化,都會(huì)聯(lián)動(dòng)的引起其他元素發(fā)生變化,這個(gè)過(guò)程叫 reflow。
4. Paint(繪制):在多個(gè)層上繪制 DOM 元素的的文字、顏色、圖像、邊框和陰影等。
5. Composite(渲染層合并):按照合理的順序合并圖層然后顯示到屏幕上。
利用 GPU 加速優(yōu)先使用渲染層合并屬性,避免 layout,paint。
從上圖可以看出,可以通過(guò)改變?cè)氐?transform 實(shí)現(xiàn)移動(dòng),伸縮變換而非改變物體的 left,top,width,height 避免 layout,paint。讓動(dòng)畫(huà)效果更加流暢。
優(yōu)化
瀏覽器渲染一個(gè)頁(yè)面大致是按照下面這個(gè)步驟執(zhí)行。
1. 獲取 DOM 并將其分割為多個(gè)層(RenderLayer)
2. 將每個(gè)層?xùn)鸥窕?,并?dú)立的繪制進(jìn)位圖中
3. 將這些位圖作為紋理上傳至 GPU
4. 復(fù)合多個(gè)層來(lái)生成最終的屏幕圖像(終極 layer )。
Chrome 開(kāi)啟查看 renderlayer
按上面的步驟之后,即可看到
1. 黃色邊框:有動(dòng)畫(huà) 3d 變換的元素,表示放到了一個(gè)新的復(fù)合層(composited layer)中渲染
2. 藍(lán)色的柵格:這些分塊可以看作是比層更低一級(jí)的單位,這些區(qū)域就是 RenderLayer
打開(kāi)一個(gè)頁(yè)面,如果該頁(yè)面的黃色邊框很多,那么肯定要查看一下原因了
Chrome 查看 layer
打開(kāi) timeline 進(jìn)行錄制,選中 timeline 的某一幀,然后選擇下面的 layer ,可以左右拖動(dòng)該模塊出現(xiàn) 3d。
我們可以看到一個(gè)頁(yè)面實(shí)際是像下面一樣組成的
從上圖不難理解,雖然我們最終在瀏覽器上看到的只是一個(gè)復(fù)印版,即最終只有一個(gè)層。類(lèi)似于PhotoShop軟件中的“圖層”概念,最后合并所有可視圖層,輸出一張圖片到屏幕上。但實(shí)際上一些dom會(huì)因?yàn)橐恍┮?guī)則被提升成獨(dú)立的層(開(kāi)啟 GPU 加速),一旦被獨(dú)立出來(lái)之后,便不會(huì)再影響其他dom的布局,因?yàn)樗淖冎螅皇恰百N上”了頁(yè)面。
根據(jù)這個(gè)優(yōu)點(diǎn),我們可以把頁(yè)面中一些布局經(jīng)常變換的dom(動(dòng)畫(huà))提升到獨(dú)立的層。那么,瀏覽器在之后的 16ms 中,只需進(jìn)行下面的幾個(gè)步驟。
目前下面這些因素都會(huì)引起Chrome創(chuàng)建合成層:
1. 3D 或透視變換(perspective,transform) CSS 屬性
2. 使用加速視頻解碼的video元素
3. 擁有 3D (WebGL) 上下文或加速的 2D 上下文的 canvas 元素
4. 混合插件(如 Flash)
5. 對(duì)自己的 opacity 做 CSS 動(dòng)畫(huà)或使用一個(gè)動(dòng)畫(huà) webkit 變換的元素
6. 擁有加速 CSS 過(guò)濾器的元素
7. 元素A有一個(gè) z-index 比自己小的元素B,且元素B是一個(gè)合成層(換句話說(shuō)就是該元素在復(fù)合層上面渲染),則元素A會(huì)提升為合成層。
上面7點(diǎn)都非常容易理解,在日常開(kāi)發(fā)中,最容易出現(xiàn)問(wèn)題的是第7點(diǎn)
元素A有一個(gè) z-index 比自己小的元素B,且元素B是一個(gè)合成層(換句話說(shuō)就是該元素在復(fù)合層上面渲染)
拿實(shí)際項(xiàng)目舉個(gè)栗子,我們按照上面的步驟開(kāi)啟 layer borders
尚未給上圖右手添加高層級(jí)的 z-index 時(shí),整個(gè)頁(yè)面在移動(dòng)端打開(kāi)后閃退。而添加了 z-index 之后,頁(yè)面正常顯示,不閃退了。
仔細(xì)看上面的 gif ,僅僅改變了 z-index ,就會(huì)改變大批數(shù)量的層(黃色邊框)
為什么 z-index 力量這么大?
我們來(lái)看一個(gè)栗子,B 在做動(dòng)畫(huà),理所當(dāng)然把B提到單獨(dú)的合成層。減少重繪。
按照上圖,我們遇到一個(gè)邏輯問(wèn)題,元素B應(yīng)該在單獨(dú)的合成層上,并且屏幕的最終圖像應(yīng)該在 GPU 上組成。但是A元素在B元素的頂部,我們沒(méi)有指定提升A元素自身層級(jí)的東西。那么瀏覽器會(huì)做什么?它將強(qiáng)制為元素A創(chuàng)建一個(gè)新的合成圖層。
這樣,A和B都被提升到單獨(dú)的復(fù)合層。
因此,使用 GPU 加速提升動(dòng)畫(huà)性能時(shí),最好給當(dāng)前動(dòng)畫(huà)元素增加一個(gè)高一點(diǎn)的 z-index 屬性,人為干擾復(fù)合層的排序,可以有效減少 Chrome 創(chuàng)建不必要的復(fù)合層,提升渲染性能。
注意:GPU 不僅需要發(fā)送渲染層圖像到 GPU ,而且還需存儲(chǔ)它們,以便稍后在動(dòng)畫(huà)中重用。別盲目創(chuàng)建渲染層,一定要分析其實(shí)際性能表現(xiàn)。因?yàn)閯?chuàng)建渲染層是有代價(jià)的,每創(chuàng)建一個(gè)新的渲染層,就意味著新的內(nèi)存分配和更復(fù)雜的層的管理。對(duì)于使用移動(dòng)設(shè)備的用戶(hù)來(lái)說(shuō)是很坑的。移動(dòng)設(shè)備沒(méi)有臺(tái)式機(jī)那么多的內(nèi)存。過(guò)多的 GPU 加速會(huì)引起頁(yè)面卡頓甚至閃退。
找到 layers,點(diǎn)擊當(dāng)前層,在右邊查看占用的 memory(內(nèi)存)
整篇文章介紹了下面幾個(gè)部分
● GPU 加速能做什么
● GPU 是什么,如何用 Chrome devtools 進(jìn)行分析 debug?
● 如何開(kāi)啟 GPU 加速?
● GPU 加速隱藏的坑–隱式合成
參考:
http://www.jianshu.com/p/a32b890c29b1
雷鋒網(wǎng)相關(guān)閱讀:
黃仁勛親自撰文懟上 TPU:P40速度比你快 2 倍,帶寬是你的 10 倍
谷歌硬件工程師揭秘,TPU為何會(huì)比CPU、GPU快30倍?
雷峰網(wǎng)版權(quán)文章,未經(jīng)授權(quán)禁止轉(zhuǎn)載。詳情見(jiàn)轉(zhuǎn)載須知。