前一篇介紹了關於 CSS DOM painting performance,再來分享個關於 CSS animation performance 相關資訊,後面會提到自己在專案上遇到相關的問題。

前一篇: cssperformance

先提一下,以下沒有要介紹 CSS animation 有哪些寫法,或是什麼炫砲特效的。

browser 60fps

先介紹一下何謂動畫卡頓感,人眼在 60 frames per seconds 更新下會是順暢感的,下方影片就是 60 fps vs 30 fps vs 24 fps 的比較,會明顯發現 fps 越低會越有卡頓感。

避免 fps 太低的方法,大概是因為避免頻繁的更新畫面,例如 scroll 搭配複雜的更新 css,或是 javascript 過度的佔用資源,或是 setInterval 頻繁更新畫面等等。

除非你用插件,否則畫面卡頓你通常會知道是為什麼,因為這些寫法都頗暴力。

避免使用過於負擔的 css style

一個頁面效果的更新,通常會有五個 thread, javascript 觸發變化,接下來會在執行 Style 計算,後面會執行 Layout 計算,再來執行 Paint 收集需要繪製的部分,最後會執行 Composite,如果前面有需要更新 paint,都收集起來更新到畫面上。

JavaScript: 利用 javasScript 執行促使畫面產生變化,例如 onClick slide,或是拖拉 element

style: 透過 browser selector ( 例如 .article-content p ),計算出 element 最後應用的 css style。

Layout: 計算 element 因為 style 套用,而可能產生的位移,並且找出相互影響的 element,或是 dom tree 變化等等。

Paint: 搜集 element 所應用的 style,並且運算出 style 結果。

Composite: 將各個 layer 做組合,並渲染更新到畫面上。

google 教學: performance rendering

render step
render step

CSS 執行部分主要分為 Layout、Paint、Composite 這三個。其中 Layout 付出的成本最高,會促使 CSSOM tree 更新重繪,至於 painting 也是高成本的計算,最節省資源的是 Composite,能幫助優化 GPU 效能。

另外 CSS 每個屬性背後所要付出的成本其實不大相同,top 跟 transform 就不一樣。top 會造成整個畫面需要重新繪製位移,負擔較大,至於 transform 則是只需要 composite。詳細每個 style 在下方連結有列出。

提醒如果你需要使用 animation 的話,盡量能用只需要 composite 的 style,避免對畫面造成可能的負擔。

文件: csstriggers

CSS animation 專案

最近完成一個有趣東西,是關於動畫效果的,需求就是要落下紅包雨,然後要讓 user 點擊開獎。在有開發時間壓力,其它種種因素之下,決定簡單用 css animation 簡單處理。

React reupdate 影響 animation

因為專案是全用 react 開發的,所以我也打算用 react 處理這個特效,在一開始是依賴 requestAnimationFrame 來針對每一組紅包 component 在動畫完成落下,再抹殺重建立產生新的動畫落下,不過因為更新過於頻繁,html dom 一直不斷在更新,造成畫面卡頓感嚴重。

那時候發現 fps 在遭遇大量的 component update 時,fps 會落到 30~40 不等,後面就直接拋棄 react 處理,直接改用 react build component,後面就不盡量不依賴 javascript 去接觸 animation 更新,我這邊是直接用 animation-iteration-count infinite,再一開始 init 計算,用一些方式製造出時間差,讓落下有錯開的錯覺。

結果就如下方,因為需求就是要多個紅包,設計還要有分離感,最後還是使用了多個 animation…。實務上務必能減少同時 animation 就減少,能減少大量效能負擔。

red envelope
壓縮 gif 後,有產生點卡頓感

Javascript 阻塞 rendering-queue

這是正在進行的專案,利用 canvas 處理圖片 (感謝同事 jason carry canvas 部分)。嘗試非常極端的情境,上傳超級大的圖片跟 webp,結果如下方 gif 畫面,有兩個 loading 引導,下方是用 css animation 的,上方是 loading gif,可以看得出來下方的 css animation 十分卡頓。

animation stuck

於是打開 chrome devtool 的 performance 去錄製,由報告中可以發現到 scripting 幾乎佔滿了整個線程,導致我們頁面的 rendering 幾乎沒資源可以執行,

另外也可以發現 CPU usage 直接跑到 100%。中間區塊的 Compositor 也像是幻燈片一樣,沒有連續的執行,很多執行中斷點。

devtool render stuck
devtool render stuck

render 更新又與 render-queue 有關,這會牽涉到 event loop。如果不大理解 event loop 的話,可以看下方這段影片,激推、神清楚。

google chrome update: browser-rendering-queue-in-depth

卡頓問題的解法可以用 web worker api,讓我們執行序 (thread) 在背景運行,這樣就避免畫面中斷與 user 互動,還可以避免畫面 fps 爆炸產生卡頓感。

但 web worker 沒支援 ie11…,也知道能不能撐受這麼大的執行運算,還在思考解法中。

mdn: web workers

google chrome update: web worker handle canvas

心得

這個議題是在專案上遇到的,才開始研究 css style animation,所以才會提到專案。畢竟由實務切入,比較能理解相關技術運用的地方,找問題,解決問題。

以上有問題歡迎留言。