이 기사는 Julian Shapiro가 작성한 CSS vs. JS 애니메이션: 어느 것이 더 빠를까요?를 번역한 것입니다. Julian Shapiro는 Velocity.js의 창시자이기도 합니다. 이것은 매우 효율적이고 간단하며 사용하기 쉬운 JS 애니메이션 라이브러리입니다. 웹애니메이션 분야에서 높은 성취도를 가지고 있다.
Javascript 애니메이션이 항상 CSS 전환만큼 빠르거나 더 빠르다는 것이 어떻게 가능합니까? 비밀은 무엇입니까? Adobe와 Google은 어떻게 미디어가 풍부한 모바일 사이트를 기본 앱만큼 빠르게 만들까요?
이 기사에서는 Javascript 기반 DOM 애니메이션 라이브러리(예: Velocity.js 및 GSAP)가 jQuery 및 CSS 기반 애니메이션 라이브러리보다 더 효율적인 이유를 단계별로 설명합니다.jQuery
기본부터 시작하겠습니다. Javascript와 jQuery는 혼동될 수 없습니다. Javascript 애니메이션은 빠른 반면 jQuery 애니메이션은 느립니다. 왜? jQuery는 매우 강력하지만 디자인 목표는 효율적인 애니메이션 엔진이 되는 것이 아니기 때문입니다.
jQuery는 레이아웃 스래싱을 피할 수 없습니다(어떤 사람들은 이를 "레이아웃 스래싱"으로 번역하기를 좋아합니다. 코드가 애니메이션에만 사용되는 것이 아니라 다른 많은 시나리오에서도 사용되기 때문에 (중복 릴레이아웃/리플로우)가 발생합니다.업데이트 작업 후 액세스 작업을 수행하면 브라우저가 페이지 요소의 스타일을 다시 계산하게 됩니다(올바른 값을 얻으려면 업데이트된 스타일을 적용해야 하기 때문입니다). 이는 일반적인 동작에서는 큰 성능 손실을 일으키지 않지만, 단 16ms 간격으로 애니메이션에 배치할 경우 상당한 성능 오버헤드가 발생합니다. 작업 순서를 조금만 변경해도 애니메이션 성능이 크게 향상될 수 있습니다.
var currentTop, currentLeft; /* 有 layout thrashing. */ currentTop = element.style.top; /* 访问 */ element.style.top = currentTop + 1; /* 更新 */ currentLeft = element.style.left; /* 访问 */ element.style.left = currentLeft + 1; /* 更新 */ /* 没有 layout thrashing. */ currentTop = element.style.top; /* 访问 */ currentLeft = element.style.left; /* 访问 */ element.style.top = currentTop + 1; /* 更新 */ element.style.left = currentLeft + 1; /* 更新 */마찬가지로 RAF를 사용하면 코드를 많이 리팩터링할 수 없습니다. RAF 사용과 setInterval 사용의 차이점을 비교해 보겠습니다.
RAF를 사용하려면 코드를 약간 수정하기만 하면 애니메이션 성능이 크게 향상될 수 있습니다.
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);
CSS 전환
CSS 전환의 애니메이션 로직은 브라우저에서 실행되므로 jQuery 애니메이션보다 성능이 좋을 수 있습니다. 장점은 다음과 같습니다.
DOM 작업 최적화 및 메모리 소비 방지로 지연 감소我的建议是:当你只在移动平台上开发,并且动画只是简单的状态切换,那么适合用纯 CSS transition。在这种情况下,transition 是高性能的原生支持方案。它可以让你将动画逻辑放在样式文件里面,而不会让你的页面充斥 Javascript 库。然而如果你在设计很复杂的富客户端界面或者在开发一个有着复杂UI状态的 app。那么我推荐你使用一个动画库,这样你的动画可以保持高效,并且你的工作流也更可控。有一个特别的库做的特别棒,它可以用 Javascript 控制 CSS transition。这就是 Transit。
所以 Javascript 可以比 CSS transition 性能更好。但是它到底有多块呢?它快到足够可以构建一个3D 动画的demo,通常需要用到 WebGL 才能完成。并且它快到足够搭建一个多媒体小动画,通常需要 Flash 或者 After Effects 才能完成。并且它还快到可以构建一个虚拟世界,通常需要 canvas 才能完成。
为了更直接的来比较主流动画库的性能,包括 Transit(使用了 CSS transition),让我们打开Velocity的官方文档。
之前那个问题还在:Javascript 是如何达到高性能的呢?下面是一个列表,列举了基于 Javascript 的动画库能做的事情:
同步DOM -> 在整个动画链中微调堆栈以达到最小的layout thrashing。
缓存链式操作中的属性值,这样可以最小化DOM的查询操作(这就是高性能 DOM 动画的阿喀琉斯之踵)
在同一个跨同层元素的调用中缓存单位转化比率(例如px转换成%、em等等单位)
忽略那些变动小到根本看不出来的DOM更新
让我们重新温习下之前学到的关于layout thrashing的知识点。Velocity.js 运用了这些最佳实践,缓存了动画结束时的属性值,在紧接的下一次动画开始时使用。这样可以避免重新查询动画的起始属性值。
$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 });
在上面的样例中,第二次调用 Velocity 时已经知道了 opacity 的起始值为 1,top 的值为 50%。
浏览器也可以使用与此类似的优化,但是要做这些事情太过激进,使用场景也会受到限制,开发者就有可能会写出有bug的动画代码。jQuery就是因为这个原因没有使用RAF(如上所说),浏览器永远不会强行实施可能打破规范或者可能偏离期望行为的优化。
最后,让我们来比较下两个Javascript框架(velocity.js 和 GSAP)。
GASP 是一个快速且功能丰富的动画平台。Velocity则更为轻量级,它大大地改善了UI动画性能和工作流程。
GSAP 需要付费才能用于商业产品。Velocity 是完全免费的,它使用了自由度极高的 MIT 协议。
性能方面,两者几乎相当,很难区分胜负。
我个人推荐在你需要如下功能时使用 GSAP:精确控制时间(例如 remapping,暂停/继续/跳过),或者需要动作(例如:贝赛尔曲线路径),又或者复杂的动画组合/队列。这些特性对游戏开发或者复杂的应用很重要,但是对普通的 web app 的 UI 不太需要。
Velocity.js
之前提到了 GSAP 有着丰富的功能,但这不代表 Velocity 的功能简单。相反的,Velocity 在 zip 压缩之后只有 7kb,它不仅仅实现了 jQuery animate 方法的所有功能,还包含了 颜色、transforms、loop、easings、class 动画和滚动动画等功能。
简单的说就是 Velocity 包含了 jQuery、 jQuery UI 和 CSS transition 的功能。
更进一步从易用性的角度来讲,Velocity 使用了 jQuery 的$.queue() 方法,因此可以无缝过渡到 jQuery 的$.animate()、$.fade()和$.delay()方法。并且 Velocity 的语法和$.animate()一摸一样,所以我们根本不需要修改页面的现有代码。
让我们快速过一下 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);
如下是一个高级用法:滚动网页到当前元素并且旋转元素。这样的动画只需要简单的几行代码:
$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 的目标是成为 DOM 动画领域性能最好易用性最高的库。这篇文章主要关注了性能方面。易用性方面可以前往 VelocityJS.org 了解。
在结束之前,请记住一个高性能的 UI 绝不仅仅是选择一个正确的动画库。页面上的其他代码也需要优化。可以看看Google那些非常棒的演讲:
Jank Free
Rendering Without Lumps
Faster Websites