在最近一個項目中,由於初期沒有作太好的規劃與人員技術能力有限,在性能方面有不少問題,而我加入這個項目的主要任務就是進行各類性能優化。其中對於重排重繪以及硬件加速相關優化進行的比較多,這種優化方式成本比較低,風險小,在配置較差設備效果明顯。此文章來之原文連接css
近些年,咱們老是聽到硬件加速,以及它如何幫助咱們提高網頁的動畫性能,讓網頁動畫變得更好,在移動端更流暢。可是我想一大部分經驗少的工程師是不知道硬件加速是如何工做的以及咱們如何使用它來幫助咱們讓動畫變得更流暢。前端
硬件加速聽起來很是複雜,像高等數學。在這篇文章中,我會簡明的講解如何在你的前端工程中使用這項技術。算法
讓咱們看一個簡單的動畫例子,一些球疊加在一塊兒。而後移動這一組球按照一個四邊形軌跡移動。最簡單的辦法就是經過設置left
和top
來實現。咱們能夠經過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」,你會發現動畫在任何桌面瀏覽器運行的都並非很順暢。若是你在移動端運行這個動畫網頁,你會看到很嚴重的丟幀現象(譯者注:其實)。爲了解決這個問題,咱們可使用transform
的translate()
函數代替對left
和top
的改變。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
並無不像操做left
和top
屬性那樣致使重繪。讓咱們看看Chrome
中的DevTools
裏面Timeline
頁面的執行結果(譯者注:在Chrome
新版本中,該工具變成了performance
)。工具
在left
和top
這個例子中,咱們能夠看到在每個步驟都有綠色柱狀圖。這是一個性能代價很高的操做。動畫會產生丟幀,這也是咱們優化動畫效果的標準。
項目看看css
的transforms
的時間線:
就像你看到的那樣,幾乎沒有綠色的柱形圖出現。
另外一個用於跟蹤重繪處理的工具是Chrome
的DevTools
中rendering
裏面的Enable paint flashing
選項。當該選項被選中,綠色的框會出如今重繪的區域。在left
和top
的例子中,當動畫運行的時候,球就有一個綠色的框,所以球就發生了重繪。
在另外一個例子中,重繪僅僅發生在動畫開始和結束的時候。
那麼transform
是如何讓動畫不會致使重繪的呢?最直接的答案就是transform
會直接使用硬件加速,在GPU
中運行,繞開了軟件渲染。
當瀏覽器接收到頁面的信息,他會將頁面解釋成DOM
輸。DOM
樹和CSS
讓瀏覽器構建渲染樹。渲染書包含渲染對象 - 在頁面中須要渲染的元素。每個渲染對象被分配到一個圖層中。每個圖層被更新到GPU
。這裏的祕訣就在於經過transform
的層會使用GPU
渲染,所以不須要重繪,就像3D圖形同樣。這個轉換是單獨處理的。
在咱們的例子中,CSS
的transform
在GPU
直接建立一個新的層。Chrome
的DevTools
的「Show layer borders」選項能夠幫助咱們查看那些是單獨的層,開啓這個選項之後單獨的層會具備一個橙色的邊框。
使用transform
樣式的球會被一個橙色的邊框所包圍,所以它在一個獨立的層中:
在此,你可能會問:何時瀏覽器會建立這種獨立的層呢?
在如下狀況會產生新的層:
CSS
的transform
屬性<video>
和 <canvas>
元素CSS
的filter
屬性z-index
提高層級你可能會想,‘等等,這個例子用的是2D轉換,並非3D轉換’。是的。這就是爲何在開始和結束的時候會有兩次重繪產生。
3D轉換和2D轉換的不一樣在因而否提早生成新的層,若是是2D的話是在實行的時候。在動畫開始的時候,一個新的層被建立,而且被傳入GPU
處理。當動畫結束,獨立的層被移除,結果被從新繪製。
GPU
渲染元素並非全部的CSS
屬性變化都會直接在GPU
處理。只有下面的屬性會這樣處理:
所以爲了頁面更加流暢,高性能的動畫,咱們須要儘量的使用GPU
來處理。
GPU
渲染在某些狀況下,它會在動畫開始的時候嘗試在GPU
渲染一個元素。這能夠幫助咱們避免建立新層的時候致使重繪。所以,咱們須要使用transform hack
技術
.example1 {
transform: translateZ(0);
}
.example2 {
transform: rotateZ(360deg);
}
複製代碼
這麼作會讓瀏覽器知道,咱們但願採用3D的方式作轉換,這會讓瀏覽器在最開始的時候就使用GPU
處理,啓動硬件加速。
這個技術也能夠用於結構複雜的元素上。讓咱們回到第一個例子,修改這個例子爲包含一個球,還有使用filter
屬性並一個具備一個背景圖片的容器。球經過left
和top
實現動畫效果。
Animating a ball with top/left properties
再一次,動畫開始丟幀。由於每一次重繪都致使了大量的性能消耗。
如今讓咱們加上transform hack
。
Animating left/top properties with hardware acceleration
如今就沒以前那麼糟糕了。爲何?由於如今背景再一個獨立的層中處理,所以重繪的代價變得很低。
天下沒有免費的午飯。對於硬件加速,目前有幾個問題。
大部分重要的問題都是關於內存。GPU
處理過多的內容會致使內存問題。這在移動端和移動端瀏覽器會致使崩潰。所以,一般不會對全部的元素使用硬件加速。
在GPU
渲染字體會致使抗鋸齒無效。這是由於GPU
和CPU
的算法不一樣。所以若是你不在動畫結束的時候關閉硬件加速,會產生字體模糊。
有必要使用transform hack
的地方是提升性能。瀏覽器自身也提供了優化的功能,這也就是will-change
屬性。這個功能容許你告訴瀏覽器這個屬性會發生變化,所以瀏覽器會在開始以前對其進行優化。這裏有一個例子:
.example {
will-change: transform;
}
複製代碼
遺憾的是,並非全部瀏覽器都支持這個功能。
概述如下咱們都講了什麼:
GPU
渲染能夠提升動畫性能GPU
渲染會提升動畫的渲染幀數GPU
渲染的CSS
屬性GPU
渲染