在 CSS 動畫中使用硬件加速(翻譯)

在最近一個項目中,由於初期沒有作太好的規劃與人員技術能力有限,在性能方面有不少問題,而我加入這個項目的主要任務就是進行各類性能優化。其中對於重排重繪以及硬件加速相關優化進行的比較多,這種優化方式成本比較低,風險小,在配置較差設備效果明顯。此文章來之原文連接css

近些年,咱們老是聽到硬件加速,以及它如何幫助咱們提高網頁的動畫性能,讓網頁動畫變得更好,在移動端更流暢。可是我想一大部分經驗少的工程師是不知道硬件加速是如何工做的以及咱們如何使用它來幫助咱們讓動畫變得更流暢。前端

硬件加速聽起來很是複雜,像高等數學。在這篇文章中,我會簡明的講解如何在你的前端工程中使用這項技術。算法

爲何須要它?

讓咱們看一個簡單的動畫例子,一些球疊加在一塊兒。而後移動這一組球按照一個四邊形軌跡移動。最簡單的辦法就是經過設置lefttop來實現。咱們能夠經過JavaScript來實現,可是咱們會使用css來實現。請注意,我沒有使用任何輔助庫,好比Autoprefixer,可是建議你在項目中使用這種庫來自動補充前綴canvas

.ball-running {
  animation: run-around 4s infinite;
}

@keyframes run-around {
  0%: {
    top: 0;
    left: 0;
  }

  25% {
    top: 0;
    left: 200px;
  }

  50% {
    top: 200px;
    left: 200px;
  }

  75% {
    top: 200px;
    left: 0;
  }
}
複製代碼

這是一個在線例子。經過按鈕來運行JavaScript啓動動畫:瀏覽器

CodePen Preview for Animating overlapping balls with top/left Properties性能優化

點擊「Start Animation」,你會發現動畫在任何桌面瀏覽器運行的都並非很順暢。若是你在移動端運行這個動畫網頁,你會看到很嚴重的丟幀現象(譯者注:其實)。爲了解決這個問題,咱們可使用transformtranslate()函數代替對lefttop的改變。app

.ball-running {
  animation: run-around 4s infinite;
}

@keyframes run-around {
  0%: {
    transform: translate(0, 0);
  }

  25% {
    transform: translate(200px, 0);
  }

  50% {
    transform: translate(200px, 200px);
  }

  75% {
    transform: translate(0, 200px);
  }
}
複製代碼

在下面例子中嘗試運行上面的代碼:ide

Animating overlapping balls with CSS transforms函數

如今動畫比之前流暢多了。很是好!那麼,爲何會這樣呢?哈,css的transform並無不像操做lefttop屬性那樣致使重繪。讓咱們看看Chrome中的DevTools裏面Timeline頁面的執行結果(譯者注:在Chrome新版本中,該工具變成了performance)。工具

lefttop這個例子中,咱們能夠看到在每個步驟都有綠色柱狀圖。這是一個性能代價很高的操做。動畫會產生丟幀,這也是咱們優化動畫效果的標準。

項目看看csstransforms的時間線:

就像你看到的那樣,幾乎沒有綠色的柱形圖出現。

另外一個用於跟蹤重繪處理的工具是ChromeDevToolsrendering裏面的Enable paint flashing選項。當該選項被選中,綠色的框會出如今重繪的區域。在lefttop的例子中,當動畫運行的時候,球就有一個綠色的框,所以球就發生了重繪。

在另外一個例子中,重繪僅僅發生在動畫開始和結束的時候。

那麼transform是如何讓動畫不會致使重繪的呢?最直接的答案就是transform會直接使用硬件加速,在GPU中運行,繞開了軟件渲染。

硬件加速如何工做的

當瀏覽器接收到頁面的信息,他會將頁面解釋成DOM輸。DOM樹和CSS讓瀏覽器構建渲染樹。渲染書包含渲染對象 - 在頁面中須要渲染的元素。每個渲染對象被分配到一個圖層中。每個圖層被更新到GPU。這裏的祕訣就在於經過transform的層會使用GPU渲染,所以不須要重繪,就像3D圖形同樣。這個轉換是單獨處理的。

在咱們的例子中,CSStransformGPU直接建立一個新的層。ChromeDevTools的「Show layer borders」選項能夠幫助咱們查看那些是單獨的層,開啓這個選項之後單獨的層會具備一個橙色的邊框。

使用transform樣式的球會被一個橙色的邊框所包圍,所以它在一個獨立的層中:

在此,你可能會問:何時瀏覽器會建立這種獨立的層呢?

在如下狀況會產生新的層:

  • 3D 或者 CSStransform屬性
  • <video><canvas> 元素
  • CSSfilter屬性
  • 覆蓋在其它元素之上的元素,好比經過z-index提高層級

你可能會想,‘等等,這個例子用的是2D轉換,並非3D轉換’。是的。這就是爲何在開始和結束的時候會有兩次重繪產生。

3D轉換和2D轉換的不一樣在因而否提早生成新的層,若是是2D的話是在實行的時候。在動畫開始的時候,一個新的層被建立,而且被傳入GPU處理。當動畫結束,獨立的層被移除,結果被從新繪製。

GPU渲染元素

並非全部的CSS屬性變化都會直接在GPU處理。只有下面的屬性會這樣處理:

  • transform
  • opacity
  • filter

所以爲了頁面更加流暢,高性能的動畫,咱們須要儘量的使用GPU來處理。

強制在GPU渲染

在某些狀況下,它會在動畫開始的時候嘗試在GPU渲染一個元素。這能夠幫助咱們避免建立新層的時候致使重繪。所以,咱們須要使用transform hack技術

.example1 {
  transform: translateZ(0);
}

.example2 {
  transform: rotateZ(360deg);
}
複製代碼

這麼作會讓瀏覽器知道,咱們但願採用3D的方式作轉換,這會讓瀏覽器在最開始的時候就使用GPU處理,啓動硬件加速。

這個技術也能夠用於結構複雜的元素上。讓咱們回到第一個例子,修改這個例子爲包含一個球,還有使用filter屬性並一個具備一個背景圖片的容器。球經過lefttop實現動畫效果。

Animating a ball with top/left properties

再一次,動畫開始丟幀。由於每一次重繪都致使了大量的性能消耗。

如今讓咱們加上transform hack

Animating left/top properties with hardware acceleration

如今就沒以前那麼糟糕了。爲何?由於如今背景再一個獨立的層中處理,所以重繪的代價變得很低。

使用硬件加速須要注意的地方

天下沒有免費的午飯。對於硬件加速,目前有幾個問題。

Memory

大部分重要的問題都是關於內存。GPU處理過多的內容會致使內存問題。這在移動端和移動端瀏覽器會致使崩潰。所以,一般不會對全部的元素使用硬件加速。

Font rendering

GPU渲染字體會致使抗鋸齒無效。這是由於GPUCPU的算法不一樣。所以若是你不在動畫結束的時候關閉硬件加速,會產生字體模糊。

The Near Future

有必要使用transform hack的地方是提升性能。瀏覽器自身也提供了優化的功能,這也就是will-change屬性。這個功能容許你告訴瀏覽器這個屬性會發生變化,所以瀏覽器會在開始以前對其進行優化。這裏有一個例子:

.example {
  will-change: transform;
}
複製代碼

遺憾的是,並非全部瀏覽器都支持這個功能。

文末

概述如下咱們都講了什麼:

  • GPU渲染能夠提升動畫性能
  • GPU渲染會提升動畫的渲染幀數
  • 使用會致使GPU渲染的CSS屬性
  • 理解如何經過「transform hack」強制讓一個元素在GPU渲染
相關文章
相關標籤/搜索