Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(译)比较CSS与JS的动画性能 #3

Open
xiangpaopao opened this issue Jun 7, 2014 · 1 comment
Open

(译)比较CSS与JS的动画性能 #3

xiangpaopao opened this issue Jun 7, 2014 · 1 comment

Comments

@xiangpaopao
Copy link
Owner

原文地址:http://davidwalsh.name/css-js-animation

基于JavaScript的动画性能竟然达到甚至超过了CSS transtions动画? Adobe和Google不断发布web App是打算挑战native App?

本文一步一步的诠释了基于JavaScript DOM的动画库(例如Velocity.js与GSAP)为何会比基于jQuery或CSS的动画库有更好的性能。

jQuery

首先,不要把JavaScript和jQuery错误的混为一谈。 jQuery展示动画会比原生JavaScript慢。 原因是尽管jQuery非常强大,但是他的目标从来不是成为一个高性能的动画引擎。

  • 由于jQuery提供了多种动画接口,所以无法避免布局抖动的问题。
  • jQuery的内存消耗频会繁触发垃圾回收机制,造成动画丢帧的问题。
  • jQuery为了保护其自身的动画机制,使用setInterval取代requestAnimationFrame.

小节:布局抖动发生在动画初始阶段,动画运行阶段js的垃圾回收会造成卡顿,而用动画帧频低是因为没有使用RAF。

实例

如何避免由DOM查询和DOM更新而引起的布局抖动。

var currentTop,
    currentLeft;

/* 产生布局抖动 */
currentTop = element.style.top; /* 查询 */
element.style.top = currentTop + 1; /* 更新 */

currentLeft = element.style.left; /* 查询 */
element.style.left = currentLeft + 1; /* 更新 */

/* 不产生布局抖动 */
currentTop = element.style.top; /* 查询 */
currentLeft = element.style.left; /* 更新 */

element.style.top = currentTop + 1; /* 更新 */
element.style.left = currentLeft + 1; /* 更新 */

查询会迫使浏览器在更新完页面后重新计算页面样式(同时考虑到新的更新的影响)。 由此产生的开销对动画运行的影响至少是16ms。

使用RAF接口并不需对现有的库进行重构。 让我们来比较一下RAF与setInterval的基础用法:

var startingTop = 0;

/* setInterval:为了达到60fps把间隔设为16ms   (1000ms/60 ~= 16ms). */
setInterval(function() {
    /* 因为此处每秒执行60次,所以我们将top属性的增量设置为1/60秒 */
    element.style.top = (startingTop += 1/60);
}, 16);

/* requestAnimationFrame: 当浏览器处于正常时,尝试以60fps运行 */
function tick () {
    element.style.top = (startingTop += 1/60);
}

window.requestAnimationFrame(tick);

结论:使用RFA会以很少的代码改动量获取更高的动画性能

CSS Transitions

CSS Transitions超越了jQuery卸载动画逻辑到浏览器本身,这有效地

1.通过优化DOM操作和内存消耗来避免卡顿;

2.使用底层的RAF机制

3.强制硬件加速(利用GPU的能力来提高动画表现)。

然而事实上,JavaScript本身也能实现这些优化。 并且GSAP一直就是这么做的。 新型动画引擎Velocity.js,在现有的技术上又进一步优化,这个我们待会讨论。

由此可见,通过对程序的改进,Javascript动画是可以和CSS动画相提并论的。 并且JavaScript动画实际上可以比他们快。

让我们思考一下CSS动画库的缺点

  • Transitions强制硬件加速消耗了GPU的资源,在高负荷的情况下导致卡顿。 这在移动设备上表现的更为明显。 (具体来说,浏览器主线程与排版线程之间数据传输时占用系统开销会造成卡顿。 一些CSS属性(如transforms和opacity)不受此种系统开销影响)。 Adobe在这里阐述了这个问题。
  • IE10以下不支持transitions,导致使用IE8和IE9来访问桌面站点很受欢迎。
  • 因为transitions不由JavaScript原生控制(JavaScript仅仅是触发他们),浏览器不知道如何通过JavaScript来优化他们。

相反:基于JavaScript动画库可以自己决定何时启用硬件加速,他们支持各版本的IE,并且他们非常适合批量优化动画。

我建议只有当你是为了移动开发并且动画只包含简单的状态变化时才使用原始的CSS transitions。 在这种情况下,transitions是一个高性能的原生解决方案,允许你保留所有动画逻辑在样式表内,从而避免使用JavaScript库。 然而,如果你在设计复杂UI或正在开发一个有多种状态UI的应用程序,总得使用一个动画库来确保你的动画性能和工作流程仍是可控的。有一个管理CSS transitions很棒的库 Transit

JavaScript动画

好吧,既然说JavaScript性能可以占据上风,那么到底快多少呢? 快到足够创建一个能与WebGL相媲美的3D动画示例;快到足够创建一个你认为要使用Flash或者After Effects才能制作出来的超炫片头。快到足够创建一个你以为canvas才能实现的虚拟世界

下面比较一下主流动画库的性能,包括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是一个高性能、功能丰富的动画引擎。 Velocity是一个轻量级UI动画工具,大大提高性能和工作流程。
  • GSAP需要商业授权。 Velocity是基于ultra-permissive MIT许可免费开源的。
  • GSAP和Velocity在实际项目中的表现难以分出高低。

我的建议当你需要精确控制时间(如重新映射、暂停/恢复/寻求)、动作(如bezier曲线路径),或复杂的分组/排序时使用GSAP。 这些特性对于游戏开发和某些领域的应用程序至关重要,但是对于web应用程序的UI就不那么重要了。

Velocity.js

GSAP的功能丰富并不意味着Velocity自身的功能弱。 相反,Velocity不仅包含了jQuery的$.animate()的所有功能 ,而且还集成了颜色动画, transforms, 循环, easings, 类动画, 滚动动画。所有的这一切,压缩后仅仅只有7KB

简而言之,Velocity相当于是把jQuery,jQuery UI,和CSS transitions结合了起来。

此外,从使用的角度来看,Velocity使用了jQuery的$.queue()方法,从而与jQuery的$.animate() , $.fade() ,$.delay() 功能无缝结合。由于Velocity的语法与$.animate() 是相同的, 所以你不需要改变你页面中的代码 。

先让我们来初步了解一下 Velocity.js 与 $.animate()的相同点 :

$element
    .delay(1000)
    /* 用Velocity控制元素的向上运动持续2000ms */
    .velocity({ top: "50%" }, 2000)
    /* 当元素向上运动的动作完成后 使用jQuery自带的方法使元素淡出 */
    .fadeOut(1000);

进一步,可以创建一个3D滚动的动画场景也只是两个简单的代码:

$element
    /* 在1000ms内将浏览器滚动到元素的上方 */
    .velocity("scroll", 1000)
    /* 然后使元素沿着他的Y轴旋转360度 */
    .velocity({ rotateY: "360deg" }, 1000);

总结

Velocity的理念是在DOM动画方面高性能、简单易用。本文侧重点是前者,在 VelocityJS.org 的头部可以更多地了解后者。

在我们结束之前,记住这一点:一个高性能的UI不仅仅需要选择正确的动画库,更重要的是优化你的页面。 你可以总谷歌的分享会中学到更多关于这些:

@stayHF
Copy link

stayHF commented Mar 31, 2016

/* 不产生布局抖动 /
currentTop = element.style.top; /
查询 /
currentLeft = element.style.left; /
更新 /
element.style.top = currentTop + 1; /
更新 /
element.style.left = currentLeft + 1; /
更新 */

第二行代码的注释写错了

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants