CSS動畫的性能優化

CSS動畫的性能優化

在Web頁面中使用動畫效果已經不是什麼稀奇的事情了。但凡優秀的UI界面都會有一些點綴用的動畫效果。舉個例子,Stripe Checkout小組通過UI動畫效果來增強支付體驗。

圖片來自@michaelvillar的Improve the payment experience with animations

可見通過UI動畫來優化用戶體驗是一件值得去做的事情。從CSS3開始,W3C開始推出了CSS transition和animation,目前他們都處於Working Draft階段。如果你需要的是簡單的狀態切換的動畫,且只針對移動端來開發,那麼我推薦你使用CSS動畫來實現。使用CSS動畫可以大大減少網頁上實現動畫效果的工作量,也可以避免引入大體積的JS動畫庫代碼。

本文主要討論的不是如何實現CSS動畫,而是如果實現一個高性能的CSS動畫效果。

瀏覽器的“硬件加速”

div {
    transform: translate3d(0, 0, 0);
}

在移動端,我們經常用到如上的CSS代碼實現所謂的“硬件加速”,來提高動畫的流暢度。在部分情況下,我們的CSS動畫的確變的更加流暢。但這個方法並不是萬能藥。當頁面中加速的元素越來越多時,網頁的性能便會下降。爲了更詳細的瞭解原因,我們有必要了解下瀏覽器的內部機制。

現代瀏覽器大都利用了GPU來加速網頁渲染。GPU是專用於圖形渲染的芯片,它擅長做如下事情:

  1. 繪製位圖到屏幕上
  2. 對圖片進行處理,例如:修改位置、旋轉和縮放等等

知道GPU擅長什麼之後,讓我們以Chrome爲例子分析下如何利用GPU來加速頁面渲染的。衆所周知,Chrome的特性之一是多進程,這樣任何一個頁面崩潰也不會影響到其他頁面。每個頁面標籤都有一個獨立的Render進程。Render進程中包含了主線程和合成線程。主線程負責:

  • Javascript的執行
  • CSS樣式計算
  • 計算Layout
  • 將頁面元素繪製成位圖(paint)
  • 發送位圖給合成線程

合成線程則主要負責:

  • 將位圖發送給GPU
  • 計算頁面的可見部分和即將可見部分(滾動)
  • 通知GPU繪製位圖到屏幕上(draw)

因爲現在頁面中通常都有很重的Javascript和CSS,所以主線程幾乎一直是滿負荷運作。如果主線程一直在運行,那麼頁面就會卡住了。至此我們可以得到一個大概的瀏覽器線程模型:

我們可以將頁面繪製的過程分爲三個部分:layout、paint和合成。layout負責計算DOM元素的佈局關係,paint負責將DOM元素繪製成位圖,合成則負責將位圖發送給GPU繪製到屏幕上(如果有transform、opacity等屬性則通知GPU做處理)。

那麼所謂的translate3d硬件加速到底做了什麼事情呢?在Chrome中當某個DOM元素開啓硬件加速之後,瀏覽器會爲此元素單獨創建一個“層”。當有單獨的層之後,此元素的repaint操作將只需要更新自己,不用影響到別人。你可以將其理解爲局部更新。所以開啓了硬件加速的動畫會變得流暢很多。

每個層都有自己對應上下文對象、位圖,而創建上下文對象和更新位圖又需要消耗內存。故當一個頁面上有太多層需要更新的時候,頁面往往會崩潰掉。你可以在Chrome中啓用chrome://flags/#composited-layer-borders,然後在開發工具中勾選Show composited layer borders。這樣就可以在頁面中看到層了。可以使用下面這個DEMO,做測試:

優化要點

我們已經知道了瀏覽器的大概機制,現在讓我們看看該從哪幾個點來入手優化我們的動畫效果。

上面已經說道主線程經常是滿負荷運作的,所以首先我們需要做的是給主線程減負。儘量把工作移動到合成線程(GPU)去完成。layout和paint操作都在主線程中完成,故我們需要減少動畫中這兩種操作。

很幸運前人已經總結了哪些CSS屬性會觸發layout和paint,詳見CSS triggers。我們需要儘量使用transform、opacity這類不觸發layout和paint操作的CSS屬性。或許你已經在不知不覺中使用了這種優化,比如使用transform:translate(10px, 10px);替代position:absolute;top:10px;left:10px;

GPU雖然擅長處理圖像,但是它也有瓶頸。連接CPU和GPU之間的帶寬是有限的,如果一次更新的層太多,則很容易就達到GPU的瓶頸,影響到動畫的流暢度。所以我們需要控制層的數量和層paint的次數。

控制層的數量可以理解,因爲層的創建和更新都會消耗內存。而控制層paint的次數,是爲了減少位圖更新的次數。每次位圖更新,合成線程就需要提交新的位圖給GPU。頻繁地更新位圖也會拖慢GPU的效率。

或許你也可能已經在不知不覺中使用了這項優化。通常在移動端做無限滾動列表的時候,我們會複用移除可視區域的列表項。只更新列表項中的數據,然後作爲新增的列表項進入用戶的視野。這樣便可以固定層的數量。

雖然限制很多、能使用的樣式比較少,但是隻要開動我們的大腦還是可以冒出很多令人驚訝的idea的。比如這個雙層疊加模擬顏色變化的方案。

總結

爲了得到更流暢的CSS動畫效果,你需要儘量做到如下條件:

  1. 動畫中儘量少使用能觸發layout和paint的CSS屬性,使用更低耗的transformopacity等屬性
  2. 儘量減少或者固定層的數量,不要在動畫過程中創建層
  3. 儘量減少層的更新(paint)次數

當然這些標準不是一定的,你需要做的是瞭解瀏覽器的機制,針對實際項目的情況來取捨。同時靈活運用手頭的工具檢查頁面的性能,例如Chrome、Browser-perf等等

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章