兄dei,聽說你動畫很卡?
回答這個問題前,我們應該要明白一個問題,為什麼我們會感到 動畫在動 ?
- 動畫的實現原理,是利用了人眼的“視覺暫留”現象,在短時間內連續播放數幅靜止的畫面,使肉眼因視覺殘象產生錯覺,而誤以為畫面在“動”。

- 大多數裝置的重新整理頻率是 60 次/秒,(1000/60 = 16.6ms)也就說是瀏覽器對每一幀畫面的渲染工作要在 16ms 內完成,超出這個時間,頁面的渲染就會出現卡頓現象,影響使用者體驗。
綜上,我們或覺得動畫卡,一個很大的原因就是因為 FPS
(幀率) 過低導致,也就是說每一幀的畫面不能的保證在 16ms 之內完成渲染工作,所以我們會覺得卡頓。
解釋清楚了會感到卡頓的原因,大家是不是很好奇每一幀的渲染過程到底發生了什麼會導致它的渲染時間超過 16ms ?

現在進入到我們的第二部分, 瀏覽器渲染繪製過程
瀏覽器渲染繪製過程
以 Webkit
的渲染流程為例分析下瀏覽器:
簡單概括為如下幾步:
-
處理HTML標記資料並生成DOM樹。
-
處理CSS標記資料並生成CSSOM樹。
-
將DOM樹與CSSOM樹合併在一起生成渲染樹。
-
Layout(佈局):計算每個 DOM 元素在最終螢幕上顯示的大小和位置。由於 web 頁面的元素佈局是相對的,所以其中任意一個元素的位置發生變化,都會聯動的引起其他元素髮生變化,這個過程叫
reflow
( 迴流 or 重排 )。 -
Paint(繪製):在多個層上繪製 DOM 元素的的文字、顏色、影象、邊框和陰影等。
-
composite(渲染層合併):按照合理的順序合併圖層然後顯示到螢幕上。
每一幀的渲染經過如上步驟,呈現在使用者的眼前,當這些步驟時間的總和 > 16ms , 使用者就會有卡頓感產生。
在部分,會重點結合 Layout
, Paint
, composite
部分來具體分析動畫卡頓的原因及優化方式,在此之前,我們先了解下強大的 chrome
提供的效能分析工具,以便我們更好分析問題。
強大的chrome效能分析工具介紹
-
performance
使用 Performance 工具時,為了規避其它 Chrome 外掛對頁面的效能影響,我們最好在無痕模式下開啟頁面
點選左上角實心圓開始錄製,看下效能分析面板。
重點介紹下圖中標紅處:
FPS:這是一個和動畫效能密切相關的指標,它表示每一秒的幀數。圖中綠色柱狀越高表示幀率越高,體驗就越流暢。若出現紅色塊,則代表長時間幀,很可能會出現卡頓。圖中以綠色為主,偶爾出現紅塊,說明網頁效能並不糟糕,但仍有可優化的空間。
CPU:表示CPU的使用情況,不同的顏色片段代表著消耗CPU資源的不同事件型別。這部分的影象和下文詳情面板中的Summary內容有對應關係,我們可以結合這兩者挖掘效能瓶頸。
summary: 渲染過程中,每個部分的耗時佔比。
-
Layers

是不是已經被這個高逼格的介面深深震撼到了!!!

重點看下 Paint flashing
, Layer borders
, FPS meter
:
-
Paint flashing: 標記當前正在重繪的元素,(元素會被一個綠色的半透明遮罩蒙上),如上圖;
-
Layer borders: 複合層
- 黃色邊框: 有動畫 3d 變換的元素,表示放到了一個新的複合層(composited layer)中渲染。
- 藍色的柵格:這些分塊可以看作是比層更低一級的單位,這些區域就是 RenderLayer。
-
FPS meter: GPU效能監控
- Frame Rate: 幀率。
- GPU Raster:GPU 光柵(預設開啟)。
- GPU Memory: GPU 使用率。
綜上就是對 chrome
一些效能分析工具的簡單介紹,說了這麼多!!!讓我們寫一些動畫實操一下吧!!!

下面就進入我們最最重要的部分!!!
動畫效能分析實戰
在瀏覽器中開啟如下程式碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> .react {position: absolute;width: 100px;height: 100px;background: #f00;animation: react-run 3s linear 0s infinite;} @keyframes react-run { 0% { top: 0px;left: 0px;} 25% {top: 0px;left: 200px;} 50% {top: 200px;left: 200px;} 75% {top: 200px;left: 0px;} 100% {top: 0px;left: 0px;} } </style> </head> <body> <div class="react"></div> </body> </html> 複製程式碼
執行結果:
-
Layers
-
performance
效能分析:
-
圖中移動的小方塊在不停的重排重繪過程中。
-
GPU 的記憶體使用率為2.4 ~ 4.9 之間。
-
一秒內重排和重繪的耗時分別為
7.3ms
和4.9ms
。
這裡還有個值得注意的點:
- 每次元素移動到光柵處,記憶體都會變大一倍從
2.4
變為4.9
。
如下圖:
圖層被光柵化後,分塊存入 GPU
的記憶體中,當元素跨光柵移動時,兩塊記憶體都在變化,所以消耗自然加倍啦!!!

好,現在讓我們開啟傳說中的 3D
引擎加速!!!
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> .react {position: absolute; width: 100px; height: 100px; background: #f00; animation: react-run 3s linear 0s infinite; transform: translate3d(0, 0, 0); } @keyframes react-run { 0% {transform: translate3d(0, 0, 0);} 25% {transform: translate3d(200, 0, 0);} 50% {transform: translate3d(200, 200, 0);} 75% {transform: translate3d(0, 200, 0);} 100% {transform: translate3d(0, 0, 0);} } </style> </head> <body> <div class="react"></div> </body> </html> 複製程式碼
執行結果:
-
Layers
-
performance
效能分析:
-
圖中移動的小方塊沒有發生重排和重繪,只有一個圖層合併。
-
GPU 的記憶體使用率幾乎為0。
-
一秒內重排和重繪的耗時分別為
0ms
和0ms
,圖層合併時間為89us
。
比較上述兩種寫法可以得處: 開啟 GPU
加速時的動畫效能要比不開啟高效。
- 未開啟
GPU
加速:
- 開啟
GPU
加速:
寫到這裡,大家可能會有疑問,為什麼開啟 GPU
加速就沒有發生重排和重繪 ?是不是所有動畫全部都開啟 GPU
加速,就會變的很快?
下面就來分析這兩個問題:
1. 為什麼開啟 GPU
加速就沒有發生重排和重繪 ?
觀察上圖:
在開啟 GPU
加速時,運動的紅色方塊開啟了新的合成層,所以不用再 重排
和 重繪
,只需要一個合成圖層的時間。
2. 是不是所有動畫全部都開啟 GPU
加速,就會變的很快?
因為開啟 GPU
加速後會建立新的圖層,新的圖層就需要一定的記憶體空間,而且圖層在合成時,圖層越多耗費的時間肯定也是越多的,所以 瘋狂的開啟 GPU
加速,不但不能解決效能問題,反而可能會帶來效能問題 。
如下 ( GPU
記憶體使用較高):


綜上所述: 合理的開啟 GPU
加速,建立新的合成層,可以給效能帶來很大的提升。