歡迎移步我的博客閱讀:《實用的 CSS — 動畫性能對比》
前言
在現代瀏覽器中,渲染頁面所要負責的線程主要有兩個:主線程和排版線程。
主線程
- 運行 JS
- 計算 HTML 元素的 CSS 樣式
- 布局頁面
- 把頁面元素繪制成一個或多個位圖
- 把這些位圖移交給排版線程
在瀏覽器開始渲染頁面,或者長時間執行某個 JS 時,主線程會一直在忙碌狀態,此時對于用戶的任何輸入或是操作都不會有所響應。
排版線程
- 通過 GPU 渲染位圖,并顯示在屏幕上
- 向主線程請求更新位圖的可見部分或即將可見的部分
- 判斷出當前頁面處于可見的部分
- 判斷出即將通過頁面滾動而可見的部分
- 隨著用戶滾動頁面來移動這些部分
排版線程對于用戶的操作保持快速的響應,普遍的效率時每秒 60 幀的速度去刷新顯示。
Transtion
下面我們在網頁中實現一個元素的高度變化的動畫,鼠標懸浮在元素上動畫啟動,直至完成:
<style>
#foo {
height: 100px;
width: 100px;
background: red;
transition: height 1s linear;
}
#foo:hover {
height: 200px;
}
</style>
<body>
<div id="foo"></div>
</body>
通過對上述代碼的觀察,讓我們來了解一下瀏覽器的兩個線程是如何協同工作的:
圖中橘黃色部分代表操作相對較慢,消耗較大;藍色部分代表操作相對較快,消耗較小
從上圖我們可以看到,瀏覽器的兩個線程在來回地切換工作,而且橘黃色出現次數較多,這意味著瀏覽器需要處理相當多的工作。
對于瀏覽器而言,由于元素的高度一直在變化,因此這個動畫的每一幀中,都需要重新布局 ——> 繪制頁面 ——> 將新的位圖加載到 GPU 中 ——> 顯示。而其中加載到 GPU
是一個相對緩慢的操作。
同時我們也在通過瀏覽器去查看元素動畫的過程,其實是由略微卡頓的現象的。
Transform
經過上面的實驗,我們對 transition
屬性有了比較好的了解;同時我們對上述動畫性能也有一個了解。接著我需要在網頁中實現一個元素的大小變化動畫,鼠標懸浮在元素上動畫啟動,直至完成:
<style>
#bar {
height: 100px;
width: 100px;
background: red;
transition: transform 1s linear;
}
#bar:hover {
transform: scale(2);
}
</style>
<body>
<div id="bar"></div>
</body>
完成上述實驗,再讓我們來看看兩個線程工作的過程:
由此我們可以看到,兩個線程來回切換的情況并不多,橘黃色部分出現的次數也較少,藍色部分居絕大部分,這意味著這個動畫效果相較于上面的要流暢很多。
在定義中,transform
是不會使瀏覽器產生重新排版的,因此 transform
不會影響原本的布局,以及周圍的元素。它會將定義的元素作為一個整體進行縮放、移動或旋轉等。
基于 transform
這類的特性,瀏覽器在渲染頁面時可以節省很多不必要的開支,例如重新布局和將位圖傳給 GPU 等工作,這樣就使得動畫更有效率。
總結
當頁面需要位移動畫時,我們有兩種方案:使用 position
或是 transalte
,而這兩種是符合上述情況的。其中 position
的位移方案與第一個符合,在動畫執行過程中會使瀏覽器重新渲染;另一外 transalte
則與第二個符合,在執行動畫時不會發生重新渲染。因此,在需要寫動畫時,我們需要選擇合適的方案,最好是選擇 scale()
、rotate()
、transalte()
等,因為他們具有更好的性能。
參考
W3C: CSS Transforms
W3C: CSS Transitions
css-animations-and-transitions-performance