CSS動畫並非絕對比JavaScript動畫性能更優越,開源動畫庫Velocity.js等就展示了強勁的性能。javascript
先開門見山的說說二者之間的區別。css
1)CSS動畫:html
基於CSS的動畫通常由瀏覽器「主線程」以外的獨立線程處理,在其中執行樣式、佈局、繪製和 JavaScript。前端
使用CSS動畫,容許對單個動畫關鍵幀、持續時間和迭代進行更多控制。html5
但缺少表現力,而且很難有意義地組織動畫,這意味着創造動畫會帶來較高的複雜度和錯誤率。java
2)JavaScript動畫:css3
在瀏覽器主線程的JavaScript中運行,主線程已經忙於運行其餘的JavaScript,樣式的計算,佈局還有繪製。線程內存在資源競爭,這實質上增長了掉幀的風險。git
基於JavaScript的動畫靈活性更高,徹底控制元素在每一個步驟,能更好的實現複雜的動畫和大量的交互(例如當要求全部的元素在頁面加載時順次加載顯示出來)github
對於多元素多步驟的動畫序列、交互拖拽動畫等,用JavaScript實現則是上選。web
1)步驟
爲了能讓動畫高性能的執行,得先了解一下頁面渲染。
頁面渲染的通常過程爲JavaScript > 計算樣式 > 佈局 > 繪製 > 渲染層合併。
Layout:計算每一個DOM元素最終在屏幕上顯示的大小和位置。頁面中一個元素的佈局發生變化,會聯動地引起其餘元素的佈局發生變化。
Paint:繪製文字、顏色、邊框和陰影等,也就是一個DOM元素全部的可視效果。這個繪製過程可能會在多個層上完成的。
Composite:在每一個層上完成繪製以後,瀏覽器會將全部層按照合理的順序合併成一個圖層,而後顯示在屏幕上。
2)優化
Layout(重排)和Paint(重繪)是整個環節中最爲耗時的兩環,因此咱們儘可能避免着這兩個環節。
爲了實現上述效果,就須要只使用那些僅觸發Composite的屬性。
能夠選擇transform和opacity,animate.css中不少的動畫都是用這兩個屬性實現的。
從Css Triggers的網頁中能夠看到兩個屬性的描述:
多層繪製方式的好處是,使用tranform來實現移動效果的元素將會被正常繪製,同時不會觸發對其餘元素的繪製。
《H5動畫60fps之路》中經過一張圖,總結了一些針對性的優化方法:
優化方法中提到了一個屬性will-change,能夠將元素提高爲合成層,不過兼容性不太友好。
對於不支持此屬性的,可使用一個3D transform屬性來強制瀏覽器建立一個新的渲染層,也就是人們常說的硬件加速。
.css{ transform: translate3d(0,0,0); }
作過一個大轉盤抽獎的項目,當使用「transform:rotate(0deg)」沒有建立一個新的渲染層,那麼就會在不停的重繪,高亮的地方就是重繪。
當改用「transform:rotate3d(0,0,1,0deg)」新增一個渲染層後,只會重繪一次,在電腦上看不出性能區別,在手機上就會很是明顯,卡的不能動。
3)工具
在Chrome瀏覽器中,有工具能夠查看到重繪與合成層。
Paint Flashing:就是看重繪,重繪的地方會高亮。
Layer Borders:黃褐色就是合成層,青色的細線是瀏覽器渲染時候的「瓦片」,瀏覽器繪製頁面的時候只會繪製可視區域必定範圍內的瓦片,以節省性能開銷。
1)CSS動畫事件
CSS動畫有兩種方式設置Transition過渡和Animation。
與過渡相關事件只有一個TransitionEnd,也就是在過渡結束後觸發。
與animation相關事件有三個,animationstart、animationiteration與animationend。
兩個動做相關的事件比較少,因此控制動畫很是有限制,應對複雜場景蠻吃力的。
2)requestAnimationFrame
requestAnimationFrame函數就是針對動畫效果的API,與顯示器固定的刷新頻率保持同步,利用這個刷新頻率進行頁面重繪,通常來講,這個頻率爲每秒60幀。
此外,一旦頁面不處於瀏覽器的當前標籤,就會自動中止刷新,這就節省了CPU、GPU和電力。
這個函數是在主線程上完成,若是主線程很是繁忙,那麼動畫效果會下降。
咱們經常使用的setInterval、setTimeout是開發者主動要求瀏覽器去繪製,由於動畫不會與屏幕的刷新率同步,極可能出現抖動和跳幀。
各個瀏覽器對此函數的支持程度不同,可能須要添加前綴,也可能須要Polyfill一下。
window.requestAnimFrame = (function() { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60); }; })();
上面的代碼按照1秒鐘60次(大約每16.7毫秒一次),來模擬requestAnimationFrame。
參考資料: