1. 程式人生 > >兄dei,聽說你動畫很卡?

兄dei,聽說你動畫很卡?

導語: 幾個星期前,因為公司活動,需要做一個比較炫酷的H5動畫, 一頓猛如虎的coding之後,發現執行起來卡成狗,當時絕望的想要出家,還好組內有經驗比較豐富的動畫大神幫忙調整,順利度過難關。

作為一個有追求的程式設計師!!!痛定思痛!!!同樣的悲劇絕對不能在同一個技術面發生兩次!!!流著淚寫下該篇總結,希望可以給大家一些啟發。

本文目錄:

為什麼動畫會有卡頓感?

回答這個問題前,我們應該要明白一個問題,為什麼我們會感到動畫在動

  1. 動畫的實現原理,是利用了人眼的“視覺暫留”現象,在短時間內連續播放數幅靜止的畫面,使肉眼因視覺殘象產生錯覺,而誤以為畫面在“動”。

  1. 大多數裝置的重新整理頻率是 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: 複合層

    1. 黃色邊框: 有動畫 3d 變換的元素,表示放到了一個新的複合層(composited layer)中渲染。
    2. 藍色的柵格:這些分塊可以看作是比層更低一級的單位,這些區域就是 RenderLayer。
  • FPS meter: GPU效能監控

    1. Frame Rate: 幀率。
    2. GPU Raster:GPU 光柵(預設開啟)。
    3. 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

效能分析:

  1. 圖中移動的小方塊在不停的重排重繪過程中。

  2. GPU 的記憶體使用率為2.4 ~ 4.9 之間。

  3. 一秒內重排和重繪的耗時分別為7.3ms4.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

效能分析:

  1. 圖中移動的小方塊沒有發生重排和重繪,只有一個圖層合併。

  2. GPU 的記憶體使用率幾乎為0。

  3. 一秒內重排和重繪的耗時分別為0ms0ms,圖層合併時間為89us

比較上述兩種寫法可以得處: 開啟GPU加速時的動畫效能要比不開啟高效。

  • 未開啟GPU加速:
  • 開啟GPU加速:

寫到這裡,大家可能會有疑問,為什麼開啟GPU加速就沒有發生重排和重繪 ?是不是所有動畫全部都開啟GPU加速,就會變的很快?

下面就來分析這兩個問題:

1. 為什麼開啟GPU加速就沒有發生重排和重繪 ?

觀察上圖:

在開啟GPU加速時,運動的紅色方塊開啟了新的合成層,所以不用再重排重繪,只需要一個合成圖層的時間。

2. 是不是所有動畫全部都開啟GPU加速,就會變的很快?

因為開啟GPU加速後會建立新的圖層,新的圖層就需要一定的記憶體空間,而且圖層在合成時,圖層越多耗費的時間肯定也是越多的,所以瘋狂的開啟GPU加速,不但不能解決效能問題,反而可能會帶來效能問題

如下 (GPU記憶體使用較高):

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

那麼,都有那些放法建立新的合成層呢?

親測有效的方法:

  • 使用css屬性:

    1. transform: translate3d(0, 0, 0);
    2. will-change: '將會發生變化的屬性';
    複製程式碼
  • 標籤:

    <video></video>
    <canvas></canvas>
    複製程式碼

總結

通過此篇文章,我們應該對動畫效能分析有一個比較系統的認識:

  1. 為什麼會有卡頓感產生?
  2. 瀏覽器每一幀的渲染工作是怎麼進行的?
  3. 如何使用chrome開發工具對動畫效能進行分析?
  4. GPU加速和普通渲染的區別在哪裡?
  5. 我們應該如何開啟GPU加速?

動畫卡頓問題的分析,不像是查詢js上的bug,有邏輯可尋,所以在遇到問題時,我們只有明白底層的原理,結合分析工具,才能更好的發現問題。