硬件加速

前言

談起瀏覽器的硬件加速,想必你們都知道的一個技巧就是在用CSS3作動畫時,給元素添加transform: translateZ(0)或者transform: translate3d(0, 0, 0)就會開啓GPU的硬件加速,將原本應該是瀏覽器處理的動畫效果轉交給GPU處理,從而使得動畫看起來更加順暢,在移動端體驗更好。本文將進一步探索其中的奧祕,例如哪些條件能夠觸發GPU硬件加速?硬件加速背後的工做原理是什麼?是否是開啓GPU硬件加速的動畫應該越多越好?
首先讓咱們來看一個動畫效果,經過CSS3的animation屬性來實現讓一個小球從坐到右移動200px的距離。如今有兩種實現方式:javascript

第一種方法經過改變該元素的top屬性來實現:css

.ball1{
  width:100px;
  height:100px;
  border-radius: 100px;
  background:red;
}
.ball1{
  animation:mymove 3s infinite linear;
  -moz-animation:mymove 3s infinite linear;
  -webkit-animation:mymove 3s infinite linear;
  -o-animation:mymove 3s infinite linear;
}

@keyframes mymove{
   from {top:0px;}
   to {top:200px;}
}

第二種方法經過translate來實現:html

.ball1{
   animation:translateMove 3s infinite linear;
   -moz-animation:translateMove 3s infinite linear;
   -webkit-animation:translateMove 3s infinite linear;
   -o-animation:translateMove 3s infinite linear;
}
@keyframes translateMove {
   from {transform: translate3d(0,0,0);}
   to {transform: translate3d(0,200px,0)}
}

兩種方式都能達到一樣的效果,但瀏覽器在內部渲染的過程卻大不相同。哪一種實現方式更優?在回答這個問題以前,咱們先來了解一下瀏覽器的渲染過程。java

瀏覽器渲染過程

已知JS是單線程工做的,可是瀏覽器能夠開啓多個線程,渲染一個網頁須要兩個重要的線程來共同完成:Main Thread 主線程 Compositor Thread 合成器線程css3

主線程作的工做:

運行JS
計算 HTML 元素的 CSS 樣式
佈局頁面
將元素繪製到一個或多個位圖中
把這些位圖交給 Compositor Thread 來處理

合成器線程作的工做:

經過 GPU 將位圖繪製到屏幕上
通知主線程去更新頁面中可見或即將可見的部分的位圖
計算出頁面中那些部分是可見的
計算出在滾動頁面時候,頁面中哪些部分是即將可見的
滾動頁面時將相應位置的元素移動到可視區

在瞭解了這兩個線程各自負責的部分以後,咱們再來看瀏覽器的渲染過程。大致流程以下:web

clipboard.png


當咱們經過某種方法引發瀏覽器的reflow時,須要從新經歷style和layout階段,致使瀏覽器從新計算頁面中每一個dom元素的尺寸及從新佈局,伴隨着從新進行repaint,這個過程是很是耗時的。爲了把代價降到最低,固然最好只留下composite這一個步驟最好。假設當咱們改變一個容器的樣式時,影響的只是它本身,而且還無需重繪,直接經過在GPU中改變紋理的屬性來改變樣式,豈不是更好?chrome

clipboard.png

如何能使元素達到這個效果?就是讓元素擁有本身的層(layer)。有了層的概念,讓咱們從層的概念再來看瀏覽器的渲染過程:canvas

  1. 獲取 DOM 並將其分割爲多個層(RenderLayer)
  2. 將每一個層柵格化,並獨立的繪製進位圖中
  3. 將這些位圖做爲紋理上傳至 GPU
  4. 複合多個層來生成最終的屏幕圖像(終極layer)

能夠將這個過程理解爲設計師的Photoshop文件。在ps源文件裏,一個圖像是由若干個圖層相互疊加而展現出來的。分紅多個圖層的好處就是每一個圖層相對獨立,修改方便,對單個圖層的修改不會影響到頁面上的其餘圖層。所以層(layer)存在的意義在於:用最小的代價來改變某個頁面元素。能夠將某個css動畫或某個js交互效果抽離到一個單獨的渲染層,來達到加速渲染的目的。瀏覽器

那麼如何才能建立一個層呢?

  • 3d transform屬性
  • backface-visibility爲hidden的元素
  • 使用加速視頻解碼的 <video> 元素
  • 擁有 3D (WebGL) 上下文或加速的 2D 上下文的 <canvas> 元素
  • 混合插件(如 Flash)
  • 對 opacity、transform、fliter、backdrop-filter 應用了 animation 或者 transition(須要是 active 的 animation 或者 transition,當 animation 或者 transition 效果未開始或結束後,合成層也會失效)
  • will-change 設置爲 opacity、transform、top、left、bottom、right(其中 top、left 等須要設置明確的定位屬性,如 relative 等)
  • 元素有一個包含複合層的後代節點(換句話說,就是一個元素擁有一個子元素,該子元素在本身的層裏)
  • 元素有一個 z-index 較低且包含一個複合層的兄弟元素(換句話說就是該元素在複合層上面渲染)

在webkit內核的瀏覽器中,若是有上述狀況,就會建立一個獨立的層(layer)。咱們能夠藉助chrome瀏覽器開發者工具中的layers和rendering結合來查看頁面中有哪些獨立的層。性能優化

性能分析

如今回到文章開始的那個動畫效果,讓咱們經過chrome的performance工具來看看具體的執行過程。(注:本次性能分析是將CPU性能將至原來的六分之一 模擬移動端的效果進行分析的。具體操做可將performance工具中的CPU選擇6*slowdown)
經過改變top屬性:

clipboard.png

clipboard.png

經過改變transform屬性:

clipboard.png

clipboard.png

從上圖能夠看出,運動的元素如何沒有獨立的層,每一幀的繪製都須要通過不停的rendering和painting過程。

但硬件加速是把雙刃劍,過渡的使用硬件加速會拔苗助長。其影響表如今:

  1. 內存。建立一個新的渲染層,須要消耗額外的內存和管理資源,若是渲染層的個數過多,很容易引發內存問題,這一點在移動端瀏覽器上尤其明顯,能夠引發電池耗電量的上升,下降電池的壽命。因此,必定要牢記不要讓頁面的每一個元素都使用硬件加速,當且僅當須要的時候才爲元素建立渲染層。
  2. 使用GPU渲染會影響字體的抗鋸齒效果。文本在動畫期間有可能會顯示的模糊。

參考文檔

  1. 無線性能優化:composite
  2. CSS動畫及硬件加速
  3. Javascript高性能動畫與頁面渲染
  4. chrome渲染優化-層模型
相關文章
相關標籤/搜索