前端性能優化之更平滑的動畫
想要達到平滑的動畫效果,瀏覽器需要避免復雜繁瑣的工作,比如解析代碼,構建渲染樹,繪制,布局等等工作。慶幸的是,我們有GPU。
從一個簡單例子說起。
假設現在有這樣的需求,鼠標移上圖標時,圖片向右移動300個像素單位,效果大概是下面這樣。
很自然,人類的理所當然思維會指引我們進坑。
.move { position: absolute; left: ; &:hover { left: 300px; } transition: all 2s; } |
沒問題,這能完成我們的任務。
讓我們打開Chrome Timeline工具Records一下。
(注意這是我故意放大很多倍圖片的效果。)
綠色部分表示瀏覽器在執行Paint操作,我們都知道動畫的刷新頻率需要保持在60fps以上肉眼才會覺得很順產。低于30fps則會讓用戶感覺到明顯卡頓。而這個實驗中,瀏覽器略感吃力。為了能夠按時完成任務,瀏覽器選擇跳過部分幀,于是我們就看到走走停停的卡頓效果,這也通常被稱為丟幀。
于是我又換成padding-left,jQuery animate方法,然而并沒有改善。
再試試translate…
.move { position: absolute; left: ; &:hover { transform: translate(300px,); } transition: all 2s; } |
我的天!質的飛躍有沒有,相同動畫下還能保持這個高的刷新頻率。
查查資料。
reflow,repaint
當我們在某個元素上執行動畫時,瀏覽器需要每一幀都檢測是否有元素受到影響,并調整他們的大小,位置,通常這種調整都是聯動的,我們稱為reflow。同樣的,瀏覽器還需要監聽元素的外觀變化,通常是背景色,陰影,邊框等可視元素,并進行重繪,我們稱為repaint。每次reflow,repaint后瀏覽器還需要合并渲染層并輸出到屏幕上。所有的這些都會是動畫卡頓的原因。
Reflow 的成本比 Repaint 的成本高得多的多。一個結點的 Reflow 很有可能導致子結點,甚至父點以及同級結點的 Reflow 。在一些高性能的電腦上也許還沒什么,但是如果 Reflow 發生在手機上,那么這個過程是延慢加載和耗電的。
——瀏覽器的渲染原理簡介
你可以在csstrigger上查找某個css屬性會觸發什么事件。
CPU,GPU分工
在沒有GUP的年代,所有任務都是CPU來完成。慶幸的是,這篇文章一開始就說到,我們生活在有GPU的年代。GPU擅長圖形計算,這是它的強項。
一個頁面上來,瀏覽器會經過一番處理,生成相應的位圖。然后把它們扔給GPU。GPU再拼接位圖,合并渲染層,并把最終結果輸出到屏幕。
問題根源
在《你不知道的Z-Index》中有提到,如果某個元素處于以下狀態:
- 當一個元素位于HTML文檔的最外層(元素)
- 當一個元素position不為initial,并且擁有一個z-index值(不為auto)
- 當一個元素被設置了opacity,transforms, filters, css-regions, paged media等屬性。
- (當然還會有其他情況)
那么就會產生一個新的渲染層(堆疊上下文),這時候執行動畫,只需要GPU按照現有的位圖,按照相應的變換在獨立的渲染層中輸出,然后再合并輸出。這個過程并不需要主線程CPU的參與。
而當我們使用left,padding,margin,JavaScript,jQuery等方式來執行動畫,那么流程就不一樣了。
還是舉上面的例子,CPU需要重新計算每一幀,元素的位置,外觀,重新定位元素,repaint,然后才生成位圖,傳給GPU渲染。
所以當我們開啟GPU渲染的時候,瀏覽器主線程就能空出來去響應用戶輸入了。
注意
然而并不是所有瀏覽器默認都會開啟GPU渲染,所以通常會用translate3d,translateZ,或者是opacity < 1等來強制開啟。
請注意,GUP渲染并不是越多越好,首先它很占內存,在移動端比較耗電, 還可能會有坑。
聯系前幾天看了前端農民工的一篇文章《CSS3硬件加速也有坑!!!》,里面的Demo也是讓我久久不能忘懷。
文中說到如果有一個元素,它的兄弟元素在復合層中渲染,而這個兄弟元素的z-index比較小,那么這個元素(不管是不是應用了硬件加速樣式)也會被放到復合層中。
暫時還不是很懂,兄弟元素如何定義。特別是看了源碼后稍稍有些困惑,div > h1
與 ul > li
為何成為兄弟元素?
所以我
無恥地求div.io邀請碼一枚。
動畫優化要點總結
- 執行動畫盡量使用CSS3 keyframes和 trainsition
- 如果需要JS執行動畫,使用requestAnimationFrame,或者Velocity,避免使用jQuery動畫,setTimeout,setInterval。
- js動畫的優點是,我們能隨時控制開始,暫停,停止,而CSS不行。缺點是沒辦法像css這樣優化,因為js動畫是在主線程上跑的。
- 動畫盡量使用transform,opacity,盡量避免left/padding/background-position等
- 盡量避免不必要的動畫發生(廢話)點擊這里
- 盡可能的為產生動畫的元素使用fixed或absolute的position
- 陰影漸顯動畫盡量用偽類的opacity來實現。點擊這里
- 使用3D硬件加速提升動畫性能時,最好給元素增加一個z-index屬性,人為干擾復合層的排序,可以有效減少chrome創建不必要的復合層,提升渲染性能,移動端優化效果尤為明顯。(來自前端農民工)
- 使用Chrome Timeline工具檢查
- 時刻把瀏覽器處理流程記在心里
推薦閱讀
- CSS3硬件加速也有坑!!!
- A Tale of Animation Performance
- How to Create Smoother Animations and Transitions in the Browser
- Why Moving Elements With Translate() Is Better Than Pos:abs Top/left
- High Performance Animations
- CSS animations and transitions performance: looking inside the browser
如需轉載,請注明出處:http://w3ctrain.com/2015/12/01/smoother-animation/