Javascript 기반 애니메이션은 비밀리에 CSS 전환만큼 효과적이거나 더 빠릅니다. 어떻게 이것이 가능합니까? Adobe와 Google이 기본 앱과 마찬가지로 성능이 뛰어난 미디어가 풍부한 모바일 사이트를 계속 출시할 때 이것이 어떻게 가능합니까?
이 기사에서는 Velocity.js 및 GSAP와 같은 Javascript 기반 DOM 애니메이션 라이브러리를 살펴보고 jQuery 및 CSS 애니메이션 효과보다 성능이 얼마나 뛰어난지 확인합니다.
jQuery
기본부터 시작해 보겠습니다. JavaScript와 jQuery는 실수로 결합되어 있습니다. JavaScript 애니메이션은 속도가 느립니다. 왜? jQuery만큼 강력한 강력한 애니메이션 엔진이 되는 것은 결코 jQuery의 디자인 목표가 아니었기 때문입니다.
레이아웃 난류는 애니메이션 시작 시 부드러움을 유발한다는 점에 유의해야 합니다. 가비지 컬렉션은 애니메이션 중에 부드러움을 유발하는 원인이며 RAF를 사용하지 않으면 프레임 속도가 낮아집니다.
구현예
레이아웃 혼란을 야기하는 DOM 쿼리 및 업데이트 조합을 피하세요.
var currentTop, currentLeft; /* With layout thrashing. */ currentTop = element.style.top; /* QUERY */ element.style.top = currentTop + 1; /* UPDATE */ currentLeft = element.style.left; /* QUERY */ element.style.left = currentLeft + 1; /* UPDATE */ /* Without layout thrashing. */ currentTop = element.style.top; /* QUERY */ currentLeft = element.style.left; /* QUERY */ element.style.top = currentTop + 1; /* UPDATE */ element.style.left = currentLeft + 1; /* UPDATE */
업데이트 후에 발생하는 쿼리는 브라우저가 페이지의 계산된 데이터를 다시 계산하도록 합니다(새로운 업데이트 효과를 고려하여). 이로 인해 애니메이션에 상당한 오버헤드가 발생하며 아주 작은 간격에 대한 런타임 초과는 16밀리초에 불과합니다.
마찬가지로 RAF 구현은 기존 코드 기반을 크게 재작업할 필요가 없습니다. RAF의 기본 구현을 setInterval과 비교해 보겠습니다.
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);
RAF 产生了推动动画性能的最大可能性,你可以对你的代码进行单一的变更.
CSS 转换
CSS转换通过把动画逻辑甩给浏览器本身去处理而超越了jQuery,这在以下几方面是有效果的:(1)优化DOM交互和内存消耗以避免卡顿(颠簸),(2)利用引擎的RAF原则,(3)强制硬件加速(利用GPU的能力来提高动画性能)。
然而,现实是,这些优化也可以在JavaScript中直接执行。GSAP已经这样做了多年。Velocity.js,一个新的动画引擎,不仅利用了同样的技术,而且还向前多走了几步——我们不久会探讨这些。
面对事实,JavaScript动画可以与CSS转换竞争只是我们康复计划的第一步。第二步是实现“JavaScript动画实际上可以比CSS转换更快”。
现在我们开始谈谈CSS变换的弱点:
相反的,基于JavaScript的动画库则可以自行确定合适开启硬件。它们原生支持各版本IE浏览器,并且它们尤其适合批量动画优化。
我的建议是仅当你单独为移动端开发且仅实现简单动画时使用原生CSS变换。这种环境下,transition是一种原生有效的解决方案,可以使你在样式表中实现所有动画逻辑,而不用添加额外的JavaScript库,从而避免你的页面变得臃肿。然而,当你在设计复杂的UI,或者是开发存在不同状态的UI的App时,你就应该使用动画库以使动画保持流畅,同时使工作流程易于管理。Transit是一个在管理CSS变换方面做得尤其优秀的库。
JavaScript 动画
好了,那JavaScript可就在性能方面占据上风了. 但Javascript究竟具体快了多少呢? 好吧 — 最初 — 对于构建一个实在的 3D动画示例 是足够快的,通常在构建中你只会看到有使用WebGL. 而构建一个 多媒体小动画 也够了,通常你看到只会使用Flash或者After Effects构建. 而构建一个 虚拟世界 也够了,通常你只会看到使用canvas构建.
为了对领先的动画库,当然还要包含Transit(它使用CSS渐变效果),进行直接的对比, 回头去看看Velocity在VelocityJS.org上的文档.
问题仍然是: JavaScript是怎样具体的达成其高水平性能的? 下面是对基于Javascript动画能够被执行这一目标的优化的一个简短清单:
回顾一下我们先前学过的关于布局颠簸的知识,Velocity.js利用这些最佳实践来缓存动画结束值以复用为随后动画的开始值,从而避免了重新查询DOM以获取元素的开始值:
$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% 开始。
浏览器本身最终能够执行许多这些相同的优化,但这样做会明显减少开发者能够制作的动画代码的方式。因此,出于同样原因,由于jQuery不使用RAF(如上所述),浏览器就不会强制优化它,甚至给出一个很小的机会去打破规格或偏离预期的行为。
最后,我们对这两个JavaScript动画库(Velocity.js 和 GSAP)互相比较一下。
GSAP 是首个动画库,用在演示JavaScript DOM 令人印象深刻的动画表现。它确实是这样,但有些缺点:
我推荐做法是在你需要精确控制定时(比如 重绘,暂停/恢复)和运动(比如贝塞尔曲线路径)的时候用 GSAP 。这些特性在游戏开发和某些特殊应用中是至关重要的,但是通常不需要用在网页应用的 UI中。
Velocity.js
引用 GSAP 丰富的特性并不代表Velocity自身在特性上是轻量级的. 相反,在压缩后仅有的7kb中,Velocity不仅仅复制了jQuery $.animate()的所有功能, 它还把颜色动画,转换,循环,easing效果,类动画还有滚动都打包了进去.
总之,Velocity是jQuery,jQuery UI,以及CSS渐变效果的最佳组合.
此外,从便利的角度看,Velocity在hood(盖子,大概意思是公共的接口)之下使用jQuery的 $.queue() 方法, 如此就可以实现同 jQuery 的 $.animate(), $.fade(), 和 $.delay() 函数的无缝互操作. 而且,由于Velocity的语法同 $.animate() 的语法是相同的, 你不需要改变页面的任何代码.
让我们快速地来看一看 Velocity.js. 在基础的层面,Velocity的行为同$.animate()一样:
$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);
在其最高级的层面,可以创建带有3D动画的复杂滚动场景 — 几乎只要用到两行简单的代码:
$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);