在網上看到一個這樣的問題: transform與position:absolute 有什么區別?查閱資料后發現這道題目其實不簡單,涉及到重排、重繪、硬件加速等網頁優化的知識。
一、問題背景
商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
原文: https://www.w3cplus.com/css3/introduction-to-hardware-acceleration-css-animations.html © w3cplus.com
商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
原文: https://www.w3cplus.com/css3/introduction-to-hardware-acceleration-css-animations.html © w3cplus.com
過去幾年,我們常常會聽說硬件加速給移動端帶來了巨大的體驗提升,但是即使對於很多經驗豐富的開發者來說,恐怕對其背后的工作原理也是模棱兩可,更不要合理地將其運用到網頁的動畫效果中了。
1、position + top/left 的效果
下面讓我們來看一個動畫效果,在該動畫中包含了幾個堆疊在一起的球並讓它們沿相同路徑移動。最簡單的方式就是實時調整它們的 left 和 top 屬性,使用 css 動畫實現。
<style> html, body { width: 100%; height: 100%;
} .ball-running { animation: run-around 4s infinite; width: 100px; height: 100px; background-color: red; position: absolute;
} @keyframes run-around { 0%: { top: 0; left: 0;
} 25% { top: 0; left: 200px;
} 50% { top: 200px; left: 200px;
} 75% { top: 200px; left: 0;
} } </style> <body> <div class="ball-running"></div> </body>
在運行的時候,即使是在電腦瀏覽器上也會隱約覺得動畫的運行並不流暢,動畫有些停頓的感覺,更不要提在移動端達到 60fps 的流暢效果了。這是因為top和left的改變會觸發瀏覽器的 reflow 和 repaint ,整個動畫過程都在不斷觸發瀏覽器的重新渲染,這個過程是很影響性能的。
2、transform 的效果
為了解決這個問題,我們使用 transform 中的 translate() 來替換 top 和 left ,重寫一下這個動畫效果。
<style> html, body { width: 100%; height: 100%;
} .ball-running { animation: run-around2 4s infinite; width: 100px; height: 100px; background-color: red; position: absolute;
} @keyframes run-around2 { 0%: { transform: translate(0, 0);
} 25% { transform: translate(200px, 0);
} 50% { transform: translate(200px, 200px);
} 75% { transform: translate(0, 200px);
} } </style> <body> <div class="ball-running"></div> </body>
這時候會發現整個動畫效果流暢了很多,在動畫移動的過程中也沒有發生repaint和reflow。
那么,為什么 transform
沒有觸發 repaint 呢?原因就是:transform
動畫由GPU控制,支持硬件加速,並不需要軟件方面的渲染。
二、硬件加速工作原理
瀏覽器接收到頁面文檔后,會將文檔中的標記語言解析為DOM樹,DOM樹和CSS結合后形成瀏覽器構建頁面的渲染樹,渲染樹中包含了大量的渲染元素,每一個渲染元素會被分到一個圖層中,每個圖層又會被加載到GPU形成渲染紋理,而圖層在GPU中 transform 是不會觸發 repaint 的,這一點非常類似3D繪圖功能,最終這些使用transform的圖層都會使用獨立的合成器進程進行處理。
在我們的示例中,CSS transform 創建了一個新的復合圖層,可以被GPU直接用來執行 transform 操作。在chrome開發者工具中開啟“show layer borders”選項后,每個復合圖層就會顯示一條黃色的邊界。示例中的球就處於一個獨立的復合圖層,移動時的變化也是獨立的。
此時,你也許會問:瀏覽器什么時候會創建一個獨立的復合圖層呢?事實上一般是在以下幾種情況下:
(1)3D 或者 CSS transform
(2)video或canvas標簽
(3)CSS filters
(4)元素覆蓋時,比如使用了 z-index 屬性
等一下,上面的示例使用的是 2D transition 而不是 3D 的 transforms 啊?這個說法沒錯,所以在timeline中我們可以看到:動畫開始和結束的時候發生了兩次 repaint 操作。
3D 和 2D transform 的區別就在於,瀏覽器在頁面渲染前為3D動畫創建獨立的復合圖層,而在運行期間為2D動畫創建。
動畫開始時,生成新的復合圖層並加載為GPU的紋理用於初始化 repaint,然后由GPU的復合器操縱整個動畫的執行,最后當動畫結束時,再次執行 repaint 操作刪除復合圖層。
三、使用 GPU 渲染元素
1、能觸發GPU渲染的屬性
並不是所有的CSS屬性都能觸發GPU的硬件加速,實際上只有少數屬性可以,比如下面的這些:
(1)transform
(2)opacity
(3)filter
2、強制使用GPU渲染
為了避免 2D transform 動畫在開始和結束時發生的 repaint 操作,我們可以硬編碼一些樣式來解決這個問題:
.exam1{ transform: translateZ(0);
} .exam2{ transform: rotateZ(360deg);
}
這段代碼的作用就是讓瀏覽器執行 3D transform,瀏覽器通過該樣式創建了一個獨立圖層,圖層中的動畫則有GPU進行預處理並且觸發了硬件加速。
3、使用硬件加速需要注意的事項
使用硬件加速並不是十全十美的事情,比如:
(1)內存。如果GPU加載了大量的紋理,那么很容易就會發生內存問題,這一點在移動端瀏覽器上尤為明顯,所以,一定要牢記不要讓頁面的每個元素都使用硬件加速。
(2)使用GPU渲染會影響字體的抗鋸齒效果。這是因為GPU和CPU具有不同的渲染機制,即使最終硬件加速停止了,文本還是會在動畫期間顯示得很模糊。
4、will-change
瀏覽器還提出了一個 will-change 屬性,該屬性允許開發者告知瀏覽器哪一個屬性即將發生變化,從而為瀏覽器對該屬性進行優化提供了時間。下面是一個使用 will-change 的示例
.exam3{ will-change: transform;
}
缺點在於其兼容性不大好。
總結:
1、transform 會使用 GPU 硬件加速,性能更好;position + top/left 會觸發大量的重繪和回流,性能影響較大。
2、硬件加速的工作原理是創建一個新的復合圖層,然后使用合成線程進行渲染。
3、3D 動畫 與 2D 動畫的區別;2D動畫會在動畫開始和動畫結束時觸發2次重新渲染。
4、使用GPU可以優化動畫效果,但是不要濫用,會有內存問題。
5、理解強制觸發硬件加速的 transform 技巧,使用對GPU友好的CSS屬性。
英文原文:http://www.sitepoint.com/introduction-to-hardware-acceleration-css-animations/