在性能上會(huì)稍微好一些,瀏覽器會(huì)對(duì)CSS3的動(dòng)畫做一些優(yōu)化(比如專門新建一個(gè)圖層用來(lái)跑動(dòng)畫)
代碼相對(duì)簡(jiǎn)單
但其缺點(diǎn)也很明顯:
在動(dòng)畫控制上不夠靈活
兼容性不好
部分動(dòng)畫功能無(wú)法實(shí)現(xiàn)(如滾動(dòng)動(dòng)畫,視差滾動(dòng)等)
JavaScript的動(dòng)畫正好彌補(bǔ)了這兩個(gè)缺點(diǎn),控制能力很強(qiáng),可以單幀的控制、變換,同時(shí)寫得好完全可以兼容IE6,并且功能強(qiáng)大。但想想CSS動(dòng)畫的transform矩陣是C++級(jí)的計(jì)算,必然要比javascript級(jí)的計(jì)算要快。另外對(duì)庫(kù)的依賴也是一個(gè)很讓人頭疼的問(wèn)題。
對(duì)于一些復(fù)雜控制的動(dòng)畫,使用javascript會(huì)比較靠譜。而在實(shí)現(xiàn)一些小的交互動(dòng)效的時(shí)候,就多考慮考慮CSS。
以實(shí)際項(xiàng)目經(jīng)驗(yàn)而言,同一個(gè)上下文,兩個(gè)方案做動(dòng)畫的效率差別不大,更加影響效率的是:
是否導(dǎo)致layout
repaint的面積
是否是有高消耗的屬性(css shadow等)
是否啟用硬件加速
動(dòng)態(tài)改變常規(guī)流元素的margin、height,將導(dǎo)致大面積layout過(guò)程,用JS或者CSS3是一樣慢的。
動(dòng)態(tài)改變?cè)氐膖ranslate3D,自然開啟了3D加速,用setTimeout/setInterval/requestAnimationFrame和CSS3沒有很大的幀速差別。
現(xiàn)今主要的不同點(diǎn)是
1. 功能涵蓋面,JS比CSS3大
定義動(dòng)畫過(guò)程的@keyframes不支持遞歸定義,如果有多種類似的動(dòng)畫過(guò)程,需要調(diào)節(jié)多個(gè)參數(shù)來(lái)生成的話,將會(huì)有很大的冗余(比如jQuery Mobile的動(dòng)畫方案),而JS則天然可以以一套函數(shù)實(shí)現(xiàn)多個(gè)不同的動(dòng)畫過(guò)程
時(shí)間尺度上,@keyframes的動(dòng)畫粒度粗,而JS的動(dòng)畫粒度控制可以很細(xì)
CSS3動(dòng)畫里被支持的時(shí)間函數(shù)非常少,不夠靈活
以現(xiàn)有的接口,CSS3動(dòng)畫無(wú)法做到支持兩個(gè)以上的狀態(tài)轉(zhuǎn)化
2. 實(shí)現(xiàn)/重構(gòu)難度不一,CSS3比JS更簡(jiǎn)單,性能調(diào)優(yōu)方向固定
3. 對(duì)于幀速表現(xiàn)不好的低版本瀏覽器,CSS3可以做到自然降級(jí),而JS則需要撰寫額外代碼
4. CSS3有兼容性問(wèn)題,而JS大多時(shí)候沒有兼容性問(wèn)題
以下會(huì)一步步告訴你為什么基于 Javascript 的 DOM 動(dòng)畫庫(kù)(比如 Velocity.js 和 GSAP)能夠比 jQuery 和基于 CSS 的動(dòng)畫庫(kù)更高效。
jQuery
讓我們從基本開始說(shuō)起: Javascript 和 jQuery 兩者不能混為一談。Javascript 動(dòng)畫很快,而 jQuery 動(dòng)畫很慢。為什么呢?因?yàn)楸M管 jQuery 異常強(qiáng)大,但是它的設(shè)計(jì)目標(biāo)并不是一個(gè)高效的動(dòng)畫引擎:
jQuery 不能避免 layout thrashing (有人喜歡將其翻譯為“布局顛簸”,會(huì)導(dǎo)致多余relayout/reflow),因?yàn)樗拇a不僅僅用于動(dòng)畫,它還用于很多其他場(chǎng)景。
jQuery的內(nèi)存消耗較大,經(jīng)常會(huì)觸發(fā)垃圾回收。而垃圾回收觸發(fā)時(shí)很容易讓動(dòng)畫卡住。
jQuery使用了setInterval而不是 reqeustAnimationFrame(RAF),因?yàn)?RAF 會(huì)在窗口失去焦點(diǎn)時(shí)停止觸發(fā),這會(huì)導(dǎo)致jQuery的bug。(目前jQuery已經(jīng)使用了RAF)
注意 layout thrashing 會(huì)導(dǎo)致動(dòng)畫在開始的時(shí)候卡頓,垃圾回收的觸發(fā)會(huì)導(dǎo)致動(dòng)畫運(yùn)行過(guò)程中的卡頓,不使用 RAF 則會(huì)導(dǎo)致動(dòng)畫幀率低。
實(shí)現(xiàn)樣例
為了避免layout thrashing,我們需要批量訪問(wèn)和更新DOM。
var currentTop, currentLeft; /* 有 layout thrashing. */ currentTop = element.style.top; /* 訪問(wèn) */ element.style.top = currentTop + 1; /* 更新 */ currentLeft = element.style.left; /* 訪問(wèn) */ element.style.left = currentLeft + 1; /* 更新 */ /* 沒有 layout thrashing. */ currentTop = element.style.top; /* 訪問(wèn) */ currentLeft = element.style.left; /* 訪問(wèn) */ element.style.top = currentTop + 1; /* 更新 */ element.style.left = currentLeft + 1; /* 更新 */
在更新操作之后的訪問(wèn)操作會(huì)強(qiáng)制瀏覽器重新計(jì)算頁(yè)面元素的樣式(因?yàn)橐獙⒏碌臉邮綉?yīng)用上去才能獲取正確的值)。這在一般操作下沒多大的性能損失,但是放在間隔僅僅16ms的動(dòng)畫中則會(huì)導(dǎo)致顯著的性能開銷。只需要稍微改動(dòng)下操作的順序就可以大大提高動(dòng)畫的性能。
類似地,使用 RAF 也不會(huì)讓你大量重構(gòu)代碼。讓我們來(lái)比較下使用 RAF 和使用 setInterval 的區(qū)別:
var startingTop = 0; /* setInterval: Runs every 16ms to achieve 60fps (1000ms/60 ~= 16ms). */ setInterval(function() { /* Since this ticks 60 times a second, we divide the top property's increment of 1 unit per 1 second by 60. */ element.style.top = (startingTop += 1/60); }, 16); /* requestAnimationFrame: Attempts to run at 60fps based on whether the browser is in an optimal state. */ function tick () { element.style.top = (startingTop += 1/60); } window.requestAnimationFrame(tick);
你只需要稍微修改下代碼來(lái)使用 RAF,就可以讓你的動(dòng)畫性能有巨大的提高。
CSS Transition
CSS transition 的動(dòng)畫邏輯是由瀏覽器來(lái)執(zhí)行,所以它的性能能夠比 jQuery 動(dòng)畫好。它的優(yōu)勢(shì)體現(xiàn)在:
通過(guò)優(yōu)化 DOM 操作,避免內(nèi)存消耗來(lái)減少卡頓
使用與 RAF 類似的機(jī)制
強(qiáng)制使用硬件加速 (通過(guò) GPU 來(lái)提高動(dòng)畫性能)
然而實(shí)際上Javascript也可以使用這些優(yōu)化。GSAP 已經(jīng)做這些優(yōu)化很久了。Velocity.js 是一個(gè)新興的動(dòng)畫引擎,它不僅僅做了這些優(yōu)化,甚至走的更遠(yuǎn)些。我們稍后會(huì)談到這些。
面對(duì)事實(shí),讓 Javascript 動(dòng)畫得以媲美 CSS 動(dòng)畫的性能只是我們偉大計(jì)劃的第一步。第二步才是重頭戲,要讓 Javascript 動(dòng)畫比 CSS 動(dòng)畫還要快!
讓我們來(lái)看看 CSS 動(dòng)畫庫(kù)的缺陷吧:
Transition 強(qiáng)制使用了 GPU 的硬件加速。導(dǎo)致瀏覽器一直處于高負(fù)荷運(yùn)轉(zhuǎn)的狀態(tài),這反而會(huì)讓動(dòng)畫變的卡頓。這在移動(dòng)瀏覽器上更為嚴(yán)重。(特別要說(shuō)明的是,當(dāng)數(shù)據(jù)在瀏覽器的主線程和合成線程之間頻繁傳輸?shù)臅r(shí)候特別消耗性能,故容易導(dǎo)致卡頓。某些 CSS 屬性,不會(huì)受到影響。Adobe 的博客談到過(guò)這個(gè)問(wèn)題。
IE 10以下的瀏覽器不支持 transition。而目前 IE8 和 IE9 還是很流行的。
transition 不能完全被 Javascript 控制(只能通過(guò) Javascript 來(lái)觸發(fā) transition),因?yàn)闉g覽器不知道如何同時(shí)讓 Javascript 控制動(dòng)畫又同時(shí)優(yōu)化動(dòng)畫的性能。
反過(guò)來(lái)說(shuō): 基于 Javascript 可以決定什么時(shí)候啟用硬件加速,它可以支持全版本的 IE,并且它完全可以進(jìn)行批量動(dòng)畫的優(yōu)化。
Javascript 動(dòng)畫
所以 Javascript 可以比 CSS transition 性能更好。但是它到底有多塊呢?它快到足夠可以構(gòu)建一個(gè)3D 動(dòng)畫的demo,通常需要用到 WebGL 才能完成。并且它快到足夠搭建一個(gè)多媒體小動(dòng)畫,通常需要 Flash 或者 After Effects 才能完成。并且它還快到可以構(gòu)建一個(gè)虛擬世界,通常需要 canvas 才能完成。
為了更直接的來(lái)比較主流動(dòng)畫庫(kù)的性能,包括 Transit(使用了 CSS transition),讓我們打開Velocity的官方文檔。
之前那個(gè)問(wèn)題還在:Javascript 是如何達(dá)到高性能的呢?下面是一個(gè)列表,列舉了基于 Javascript 的動(dòng)畫庫(kù)能做的事情:
同步DOM -> 在整個(gè)動(dòng)畫鏈中微調(diào)堆棧以達(dá)到最小的layout thrashing。
緩存鏈?zhǔn)讲僮髦械膶傩灾担@樣可以最小化DOM的查詢操作(這就是高性能 DOM 動(dòng)畫的阿喀琉斯之踵)
在同一個(gè)跨同層元素的調(diào)用中緩存單位轉(zhuǎn)化比率(例如px轉(zhuǎn)換成%、em等等單位)
忽略那些變動(dòng)小到根本看不出來(lái)的DOM更新
讓我們重新溫習(xí)下之前學(xué)到的關(guān)于layout thrashing的知識(shí)點(diǎn)。Velocity.js 運(yùn)用了這些最佳實(shí)踐,緩存了動(dòng)畫結(jié)束時(shí)的屬性值,在緊接的下一次動(dòng)畫開始時(shí)使用。這樣可以避免重新查詢動(dòng)畫的起始屬性值。
$element /* Slide the element down into view. */ .velocity({ opacity: 1, top: "50%" }) /* After a delay of 1000ms, slide the element out of view. */ .velocity({ opacity: 0, top: "-50%" }, { delay: 1000 });
在上面的樣例中,第二次調(diào)用 Velocity 時(shí)已經(jīng)知道了 opacity 的起始值為 1,top 的值為 50%。
瀏覽器也可以使用與此類似的優(yōu)化,但是要做這些事情太過(guò)激進(jìn),使用場(chǎng)景也會(huì)受到限制,開發(fā)者就有可能會(huì)寫出有bug的動(dòng)畫代碼。jQuery就是因?yàn)檫@個(gè)原因沒有使用RAF(如上所說(shuō)),瀏覽器永遠(yuǎn)不會(huì)強(qiáng)行實(shí)施可能打破規(guī)范或者可能偏離期望行為的優(yōu)化。
最后,讓我們來(lái)比較下兩個(gè)Javascript框架(velocity.js 和 GSAP)。
GASP 是一個(gè)快速且功能豐富的動(dòng)畫平臺(tái)。Velocity則更為輕量級(jí),它大大地改善了UI動(dòng)畫性能和工作流程。
GSAP 需要付費(fèi)才能用于商業(yè)產(chǎn)品。Velocity 是完全免費(fèi)的,它使用了自由度極高的 MIT 協(xié)議。
性能方面,兩者幾乎相當(dāng),很難區(qū)分勝負(fù)。
Velocity.js
之前提到了 GSAP 有著豐富的功能,但這不代表 Velocity 的功能簡(jiǎn)單。相反的,Velocity 在 zip 壓縮之后只有 7kb,它不僅僅實(shí)現(xiàn)了 jQuery animate 方法的所有功能,還包含了 顏色、transforms、loop、easings、class 動(dòng)畫和滾動(dòng)動(dòng)畫等功能。
簡(jiǎn)單的說(shuō)就是 Velocity 包含了 jQuery、 jQuery UI 和 CSS transition 的功能。
更進(jìn)一步從易用性的角度來(lái)講,Velocity 使用了 jQuery 的$.queue() 方法,因此可以無(wú)縫過(guò)渡到 jQuery 的$.animate()、$.fade()和$.delay()方法。并且 Velocity 的語(yǔ)法和$.animate()一摸一樣,所以我們根本不需要修改頁(yè)面的現(xiàn)有代碼。
讓我們快速過(guò)一下 Velocity.js 的例子:
$element .delay(1000) /* Use Velocity to animate the element's top property over a duration of 2000ms. */ .velocity({ top: "50%" }, 2000) /* Use a standard jQuery method to fade the element out once Velocity is done animating top. */ .fadeOut(1000);
如下是一個(gè)高級(jí)用法:滾動(dòng)網(wǎng)頁(yè)到當(dāng)前元素并且旋轉(zhuǎn)元素。這樣的動(dòng)畫只需要簡(jiǎn)單的幾行代碼:
$element /* Scroll the browser to the top of this element over a duration of 1000ms. */ .velocity("scroll", 1000) /* Then rotate the element around its Y axis by 360 degrees. */ .velocity({ rotateY: "360deg" }, 1000);
Velocity 的目標(biāo)是成為 DOM 動(dòng)畫領(lǐng)域性能最好易用性最高的庫(kù)。這篇文章主要關(guān)注了性能方面。易用性方面可以前往 VelocityJS.org 了解。
請(qǐng)記住一個(gè)高性能的 UI 絕不僅僅是選擇一個(gè)正確的動(dòng)畫庫(kù)。頁(yè)面上的其他代碼也需要優(yōu)化。
聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com