當(dāng)我們要在頁面上實現(xiàn)一些動畫效果的時候,通常會考慮兩種方式:
1、通過css3的animation+keyframes,或者transition
2、通過setTimeout或者setInterval。
毫無疑問,首選的方案是第一種。而當(dāng)需要考慮兼容性,或者需要精確的控制動畫的時候,無可避免地會使用setTimeout/setInterval來實現(xiàn)動畫。這種實現(xiàn)方式是低效的,而且在當(dāng)前時間點,可以考慮更高效的實現(xiàn)方式。
什么是流暢
頁面的每一幀都是系統(tǒng)通過CPU或者GPU繪制出來的,其繪制的最高幀率受限于顯示器的刷新頻率。鑒于大多數(shù)的顯示器刷新頻率都是60Hz,頁面的最大繪制幀率就是60fps(frame per second)。
因此一個動畫最理想的情況就是60fps。這就意味著我們需要在把每一幀的繪制時間限制在16.7毫秒(1000/60),壓力山大。
setTimeout/setInterval的問題
首先,計時并不精確。setTimeout和setInterval的計時依賴瀏覽器內(nèi)置時鐘,而內(nèi)置時鐘的精確度又依賴于時鐘的更新頻率,在IE8及以下版本的瀏覽器中,這個更新頻率是15.6ms。也就意味著,如果把timer的間隔設(shè)成16.7ms,我們需要經(jīng)歷兩個時鐘更新周期才會觸發(fā)timer,延時了14.5ms(15.6 * 2 – 16.7)。
其次,由于單線程與異步隊列,當(dāng)動畫兩幀之間有一個復(fù)雜任務(wù)時,第二幀的繪制會一直等待這個任務(wù)完畢才會開始,必然會帶來卡頓現(xiàn)象。
假使計時是精確的(以14ms為例),任務(wù)隊列也不存在阻塞狀態(tài),是不是就沒問題了?我們看下圖,紅色幀丟失。
requestAnimationFrame
requestAnimationFrame
這里并不討論requestAnimationFrame的定義,具體可以看:
https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestAnimationFrame
從上面的描述中,我們可以總結(jié)出影響一個動畫的兩個關(guān)鍵因素:
1、開始繪制一幀的時機。
2、繪制一幀需要的時間。
requestAnimationFrame這個原生API可以自動幫我們設(shè)置動畫的幀率,我們只需要告訴它,我們需要繪制新的一幀,請在繪制下一幀的時候調(diào)用我的回調(diào)函數(shù)。這保證了上面的關(guān)鍵因素的第一點。對于第二點requestAnimationFrame是沒有辦法的,但是它可以在繪制時間比較長的時候,把動畫幀率從60fps變?yōu)?0fps以保證動畫不卡頓。
requestAnimationFrame還處于草案階段,瀏覽器的兼容性也有限:
Polyfill
requestAnimationFrame
不同的瀏覽器對于requestAnimationFrame定義存在差異,而且對于不支持該特性的瀏覽器,我們需要降級到setTimeout的方案。好在requestAnimationFrame和setTimeout的定義一致,比較方便做兼容。
兼容方法已經(jīng)有成熟的實現(xiàn)。
關(guān)于Layer
requestAnimationFrame
我們以Apple的主頁為例:
最上面的圖片輪轉(zhuǎn)動畫里,每一頁都設(shè)置了transform:translateZ(0px)。為什么可以使用2D渲染就能實現(xiàn)的效果卻要用3D?這就跟瀏覽器的渲染機制有關(guān)。
首先,使用3D的話,瀏覽器會調(diào)用GPU而不是CPU來進行頁面渲染,效率更高。
其次,瀏覽器渲染一個頁面的過程中,會把頁面元素分成若干的層(Layer),然后按層提交給GPU做貼圖渲染。當(dāng)我們要改變某一個DOM的樣式時,比如width或者h(yuǎn)eight,會觸發(fā)該DOM所在層的重繪。一次操作還好,但是如果是頻繁改變的動畫,效率就會非常低。
Apple這里使用translate3d,就是為了單獨為動畫頁建立Layer,進而提高重繪的效率。
總結(jié)
精度低的動畫盡量使用css3來實現(xiàn)。
如果需要js來實現(xiàn)動畫,可以使用requestAnimationFrame。有兼容需求可以引入polyfill。
盡量把動畫DOM放在獨立的Layer中。
本文作者:高原(點融黑幫),現(xiàn)任點融成都團隊高級前端開發(fā),四川大學(xué)計算機碩士。三年創(chuàng)業(yè),一年外企經(jīng)驗,專注于研究前端的各種框架與新技術(shù)。業(yè)余喜歡打籃球,司職中鋒。