CSS vs. JS Animation: 哪個更快
CSS vs. JS Animation: 哪個更快?
CSS vs. JS Animation: 哪個更快?基於JavaScript的動畫竟然已經默默地比CSS的transition動畫快了?而且,Adobe和 Google竟然一直在發布可以媲美原生應用的富媒體移動站點?
這篇文章將會逐點講解基於JavaScript的DOM動畫庫,比如Velocity.js和GSAP,是如何比jQuery和基於CSS的動畫庫高效的。
jQuery
讓我們先從這個事實開始:JavaScript和jQuery被錯誤的混淆了。JavaScript的動畫是快的,但是jQuery的動畫慢。為什麽?因為雖然jQuery很強大,但是它的目標從來不是為了成為一個高效的動畫引擎。
jQuery不能避免布局震蕩因為它的代碼除了動畫還提供了很多功能。
jQuery的內存消耗經常觸發垃圾回收,導致動畫卡住
jQuery使用setInterval而不是requestAnimationFrame (RAF)為了避免一些bug
註意,布局震蕩引起了動畫開始處的卡頓,垃圾回收導致了動畫進行中的卡頓,RAF的缺席導致了幀率低。
實現的例子
避免布局震蕩,包括簡單地合並DOM查詢和DOM更新:
var currentTop, currentLeft; /* 有布局震蕩 */ currentTop = element.style.top; /* QUERY */ element.style.top = currentTop + 1; /* UPDATE */ currentLeft = element.style.left; /* QUERY */ element.style.left = currentLeft + 1; /* UPDATE */ /* 沒有布局震蕩 */ currentTop = element.style.top; /* QUERY */ currentLeft = element.style.left; /* QUERY */ element.style.top = currentTop + 1; /* UPDATE */ element.style.left = currentLeft + 1; /* UPDATE */
發生在更新之後的查詢會強制瀏覽器立馬重新布局,並計算給出頁面樣式的計算值(把更新的影響考慮在內)。這對於運行於16ms間隔的動畫來講,會產生巨大的開銷。
同樣,實現RAF並不需要對既有代碼改動很大。讓我們來對比一下RAF的實現和setInterval的實現:
var startingTop = 0; /* setInterval: 每16ms運行一次來達到60fps (1000ms/60 ~= 16ms). */ setInterval(function() { /* 由於這裏的代碼會在1s內執行60次,所以我們把top屬性每秒1單位的增長分成60份 */ element.style.top = (startingTop += 1/60); }, 16); /* requestAnimationFrame: 不管瀏覽器是否處於最優狀態,都試圖運行在60fps */ function tick () { element.style.top = (startingTop += 1/60); } window.requestAnimationFrame(tick);
RAF極大限度地提高了動畫的性能。而您只需要修改為數不多的代碼。
CSS Transitions
CSS transitions的動畫性能優於jQuery,它把動畫的邏輯交給了瀏覽器本身。這會有助於:1)優化DOM交互和內存消耗以避免卡頓,2)在底層借助RAF的特性,3)強制硬件加速(借助GPU的能力來提高動畫性能)。
然而,實際情況是,這些優化可以直接通過JavaScript來實現,GSAP已經致力於此多年。Velocity.js,一個新的動畫引擎,不止借助於上述技術,還應用了其他方法--我們將很快探討。
明白JavaScript動畫可以媲美CSS動畫庫這一事實,只是我們計劃的第一步。第二步是我們要明白JavaScript動畫可以比CSS動畫還快。
讓我們從檢查CSS動畫庫的缺陷開始:
Transitions的強制硬件加速是使GPU加速,然而這反而會導致GPU強壓狀況下動畫的卡頓。這些影響在移動設備上更為嚴重。(特別地,這個卡頓是由於數據在瀏覽器的主線程和排序線程間傳遞的開銷導致的。一些CSS屬性,比如transforms和opacity,是不受這個開銷影響的。)Adobe在這裏闡述了這個問題。
Transitions在IE10以下有兼容問題, 這在PC端站點會很容易導致問題發生,因為IE8和IE9依然很流行。
因為transitions並不是被JavaScript控制(它們只是被JavaScript觸發),瀏覽器並不知道如何同步地使用JavaScript代碼來操控優化transitions。
相反地:基於JavaScript的動畫庫,可以自己決定什麽時候使用硬件加速,可以兼容所有版本的IE,並且它們非常適合批量動畫優化。
我的建議是,當您只是開發移動站點,並且您的動畫只包含簡單的狀態變化時,可以使用原生CSS transitions。在這種情況下,transitions算是一種高效並且原生的解決方案,並且可以把所有的動畫邏輯只放在css中,避免了因為引入JavaScript庫而導致頁面臃腫。但是,如果您正在設計復雜的UI,或者正在開發具有狀態UI的應用程序,請使用JavaScript動畫庫,它可以使您的動畫保持高性能,使您的工作流程保持可控。特別是在管理CSStransitions方面做得很棒的一個庫是 Transit。
JavaScript Animation
Okay,所以JavaScript在性能上可以占上風。但是JavaScript究竟可以快多少呢?其實,它已經快到可以創建復雜的,通常只能用WebGL構建的3D animation demo。已經快到可以創建通常只能用Flash或者影效處理做到的multimedia teaser。已經快到可以創建通常只能用canvas構建的virtual world。
為了直觀比較動畫庫的領先性能,包括Transit(內部使用CSS transitions),請查閱Velocity的文檔,在VelocityJS.org。
依然存在問題:JavaScript究竟如何達到高性能?下面是基於JavaScript的動畫庫能實現的優化列表:
為了減小布局震蕩,將整個動畫中涉及到DOM同步化到堆棧中。
緩存鏈式調用中的屬性值,以盡量減少DOM查詢(它是影響DOM動畫性能的致命弱點)的發生。
在同一個跨同級元素調用中緩存單位轉換比率(例如PX到%、em等)。
當樣式更新在視覺上不明顯時,跳過更新。
回顧之前講的布局震蕩,Velocity.js利用這些最佳實踐來緩存動畫的結束值,這些值會被重用為之後動畫的開始值,從而避免再次查詢DOM元素的初始值:
$element
/* 將元素向下滑動到視圖中。 */
.velocity({ opacity: 1, top: "50%" })
/* 延遲1000ms,元素滑動出視圖 */
.velocity({ opacity: 0, top: "-50%" }, { delay: 1000 });
在上面的例子中,第二個Velocity自動知道它應該從opacity為1,top為50%開始。
瀏覽器最終可以自己執行很多相同的優化,但這樣做將需要極大地限制開發人員編寫動畫代碼的方式。因此,同樣的原因,jQuery不使用RAF(見上文),瀏覽器也永遠不會強加優化,即使這些優化只有非常小的可能會打破規範或偏離預期的行為。
最後,讓我們來比較一下這兩個JavaScript動畫庫(Velocity.js和GSAP)。
GSAP是一種快速、功能豐富的動畫平臺。Velocit是一個輕量級工具,可以極大地提高UI動畫性能和工作流程。
GSAP需要許可費。Velocity是通過許MIT開源的。
性能都很優異,GSAP和Velocity在真實項目中沒有區別。
我的建議是:當您需要精確的控制(例如重映,暫停/恢復/搜索)、運動(例如Bezier曲線路徑),或復雜的分組/排序時,使用GSAP。這些特性對於遊戲開發和某些niche應用非常重要,但在Web應用程序的UI中並不常見。
Velocity.js
定位GSAP功能豐富,並不意味著Velocity功能單一。相反地,在壓縮後只有7Kb的文件中,Velocity不僅提供了jQuery$.animate()
的所有功能,而且提供了color animation,transforms,loops,easings,class animation和scrolling。
簡而言之,Velocity是jQuery、jQuery UI和CSStransitions的最佳組合。
進一步,從方便的角度,Velocity在底層使用jQuery的$.queue()
方法,因此可以無縫地與jQuery的$.animate()
, $.fade()
和$.delay()
函數交互。並且,由於Velocity的語法和$.animate()
一致,您頁面的代碼不需要修改。
讓我們快速看一下Velocity.js。在基礎動畫上,Velocity和$.animate()
一樣:
$element
.delay(1000)
/* 使用Velocity的2000ms內改變元素top屬性的動畫*/
.velocity({ top: "50%" }, 2000)
/* 當上面Velocity動畫執行完時,使用標準的jQuery方法來使元素淡出*/
.fadeOut(1000);
在高級動畫上,復雜的滾動場景和三維動畫都可以創建——只需要兩行簡單的代碼:
$element
/* 在1000ms內,瀏覽器滾動到這個元素的頂部 */
.velocity("scroll", 1000)
/* 之後使元素繞著它的Y軸旋轉360度。 */
.velocity({ rotateY: "360deg" }, 1000);
結束語
Velocity的目標是保持領先的DOM動畫性能和便捷。本文的重點是前者。請去VelocityJS.org學習更多關於後者的知識。
在我們結束之前,記得_*一個高性能的UI不僅僅是選擇合適的動畫庫_。頁面的其余部分也應該優化。從下面這些奇妙的Google話題中學習更多:
Jank Free
Rendering Without Lumps
Faster Websites
本文轉載自:眾成翻譯
譯者:凱小凱
審校: betsey
鏈接:http://www.zcfy.cc/article/4635
原文:https://davidwalsh.name/css-js-animation
CSS vs. JS Animation: 哪個更快