你們好、這裏就是用了大量動畫去作我的主頁、還有射擊遊戲的 yuki
。javascript
感受 css 的動畫用的太多了,個人 MacBook 都要「罷工」了,回過頭來,我就給你們總結一下,個人實驗結果吧。(仍是好想用 css 動畫css
這篇文章的目的大概就是,在網頁裏用 css 動畫加一點複雜的動畫,而後去作一些遊戲或者藝術表現的時候,所有都用上 css 的 will-change 屬性,怎麼樣才能讓 GPU 渲染最合適。html
若是你要問 will-change 是什麼?那能夠看一下我放在這篇文章裏的參考列表。vue
css-anime.firebaseapp.com/java
本次要驗證的動畫有下面幾個點:css3
不知道你們有沒有遇到過,須要一邊作動畫,還得一直加標籤,而後還得一直顯示,且不能從畫面消失,這種很痛苦的狀況。git
實驗環境以下:github
以後會提一下 Safari 下的狀況。 由於不一樣的環境會有很大的差別,瀏覽器和操做系統不同的話,結果會有差別,我先在這裏事先說明一下。web
譯者注:下面連接所有都是*日文*的,標題我翻譯了一下,日文 ok 的同窗能夠點點看... 瀏覽器
這篇文章參考如下連接:
爲了下手方便,我就先隨便的寫了一下,我把代碼貼也上來了,你們看到代碼應該也就就明白了。
花❀的話 html 部分大概就是這樣的,爲了輕鬆和我的興趣,這裏用了下 Vue,其實什麼框架都是能夠的。
Flower.vue
<div class="flower-root" :class="{animate: visible}" @animationend="onEndAnim">
<!-- 花瓣 -->
<div class="petal" v-for="(petal, index) in petals" :key="index" :style="{ transform: `rotate(${petal.r}deg)`, 'background-color': petal.col }">
</div>
<!-- 中間 -->
<div class="center"></div>
</div>
複製代碼
花瓣是用 div 搭的,除了角度和顏色是在 template 裏面寫的,其它的都是在下面 <style> 標籤裏寫的。開花的動畫也在這裏寫了(也就是說,動畫跟 Vue 和 javascirpt 沒有關係,就只是用 css 寫的而已)。
Flower.vue
<style lang="scss" scoped>
.flower-root {
position: absolute;
transform: rotate(0deg) scale(0);
animation: rotate 2s ease-out 0s 1 normal forwards;
}
.petal {
position: absolute;
width: 70px;
height: 20px;
top: -10px;
left: 0;
transform-origin: left center;
border-radius: 50px;
background-color: #ffb7aa;
}
.center {
position: absolute;
width: 30px;
height: 30px;
left: -15px;
top: -15px;
border-radius: 30px;
background-color: #ffe683;
}
// 一邊旋轉一遍變大
@keyframes rotate {
0% { transform: rotate(0deg) scale(0); }
100% { transform: rotate(360deg) scale(1); }
}
</style>
複製代碼
寫出來的花大概就是這種感受。
讓這個 Flower 組件每隔必定時間,就往畫面里加,而後畫面的總體讓它轉起來。
先試試不用 will-change,跑一下動畫,打開 Chrome 的 performance monitor 面板。
譯者注:能夠 f12 -> ctrl + shift + p 輸入 rendering 打開 fps 面板,圖片上左側的 gpu 面板是 mac 系統自帶的。
CPU 還挺給力的,還保持着 60 fps,哦喲,好像還不錯喲。
500 個了。差很少 400 個左右的時候 CPU 已經到達瓶頸了,幀數一會兒就掉下來了,瀏覽器上了 GPU,可是幀數好像仍是很不穩定。
最後,平均幀數到了差很少 20fps,風扇一直轉的大聲...
全部的花都開完了,只剩下 1000 朵靜止的花在旋轉。到這一步,終於回覆了 60fps。
可是不試試看怎麼會知道呢?變動的地方就是在 style 里加上 will-change: transform。
Flower.vue
.flower-root {
position: absolute;
transform: rotate(0deg) scale(0);
animation: rotate 2s ease-out 0s 1 normal forwards;
will-change: transform; // 追加
}
複製代碼
Let's start!
第 100 個,很是的流暢,CPU 負荷也很低。
500 個,開始有點掙扎了,GPU 就像吃了芥末同樣,一會兒就竄起來了,CPU 也有點負荷了。
快到結尾了,CPU 和 GPU 都在瘋狂的「慘叫」,performance 也是很危險的狀態。
全部的開花的動畫都結束了以後,負荷也不會降下來, 寫了 will-change 的話,瀏覽器爲了保證接下來的隨時會開始的動畫的性能,就算動畫結束了,負荷也不會降低。
譯者注:把 will-change 屬性刪除是指把 will-change 的指設置成默認的 auto,以後的翻譯也都是如此。
實驗 2 的問題是,開花的動畫明明都已經結束了,可是 will-change 卻存在着。所以,在實驗 3,開花的動畫結束後,就把 will-change 樣式刪除。
爲了可以動態的設置 will-change 屬性,因此把這個屬性挪到 template 裏了,而後加個變量去控制它(在 Vue 加了個 isMoving 變量)。監聽 animationend 事件,在這個事件內,去改 isMoving 變量。
Flower.vue
<div class="flower-root" :style="{ 'will-change': isMoving ? 'transform' : 'auto', }" @animationend="onEndAnim">
...
複製代碼
Flower.vue
private onEndAnim() {
this.isMoving = false
}
複製代碼
檢測到動畫結束,只把 will-change 的值刪除。
那麼,start!
第 100 個,感受不錯...
500 個,嗯?CPU 好吃力啊。幀數也差很少 300 個左右的時候就降下來了。由於動畫結束了就把 will-change 給刪了,GPU 負荷是輕了,可是 CPU 負擔卻重了。
到最後,差很少要「罷工」了。
全部的動畫結束了以後,終於回覆到 60fps 了。
好奇怪,結果並無那麼明朗。 我只是在動畫結束後立刻把 will-change 的值刪了而已,到底發生了什麼?
看了下 Chrome 的 performance 的狀況,Update Layer Tree 很迷,隔一段時間執行一次。
這是 Chrome 內部的處理,我也不太知道具體是爲何...
看 DevTools 的 Timeline 面板、理解瀏覽器的渲染機制(日文連接)
Update Layer Tree
GPU 更新着的執行處理的 layer
就是說,GPU 爲了執行處理,把須要處理的元素放到 layer 上,把不須要處理的元素從 layer 刪掉,而後從新構建。
此次試錯的結果,如今的這個狀況,Chrome 會有如下會傾向的點:
我沒有看源碼,這些也只是個人猜想。
在實驗 3 中,動畫結束,一個一個的去刪 will-change 的值,效果會很是的糟糕。不知道爲何 Chrome 要在這個點上這麼的「努力」啊,我試試看有沒有其它辦法繞一下。
既然執一次一次去刪 will-change 的話,會讓 Update Layer Tree 產生負擔,那麼 100 個的話也是同樣的吧。那麼在攢必定數量以後一口氣的去刪掉的話會是怎麼樣呢?立刻來試試看。
準備好 Flower 隊列
Flower.vue
const queueLimit = 100
const stopedFlowers: Flower[] = []
複製代碼
動畫結束後,加到隊列裏, 到 100 個了以後,設置 isMoving 的值,一口氣刪掉 will-change 的值。
Flower.vue
private onEndAnim() {
stopedFlowers.push(this)
if (stopedFlowers.length === queueLimit) {
stopedFlowers.forEach(fl => fl.isMoving = false)
stopedFlowers.length = 0
}
}
複製代碼
感受有點像是在投機取巧,哈哈。實驗開始!
第 100 個,這個時候動畫纔開始,will-change 所有都是帶着的,也就是說,如今的這個狀態跟實驗 2 是同樣的。
第 500 個,以 100 個爲單位,刪除 will-change,因此會隔一段時間,幀數會掉一下。
到最後這種狀況會一直持續,雖然會有那麼一瞬間幀數會卡一下,單總的來講,負荷仍是很平衡的。
安全跑完,最後 100 個結束了以後,全部的元素都會刪掉 will-change 的值。這時候,跟實驗 1 和實驗 3 的狀態是同樣的。
在 MacOS/iOS 的 Safari 跑了下,實驗 3 的結果很是的流暢。
也就是說,若是不是 Chrome 的 bug 的話,難道是我使用的問題?由於沒有看源碼,因此也不太肯定。若是有知道的同窗,歡迎在評論區評論。
以前只是知道 will-change 會讓瀏覽器啓用 gpu 渲染,沒想到這個使用多了,不必定就會爽了,使用 will-change 也是有不少須要注意的點。若是在須要大量使用動畫的狀況下,能夠參考下這篇文章的作法,適當的去刪掉 will-change 屬性。