classicemi / blog

🖋 my personal blog
https://wushuang.name/
32 stars 2 forks source link

消除疑问:CSS动画 VS JavaScript #3

Open classicemi opened 10 years ago

classicemi commented 10 years ago

原文地址:http://css-tricks.com/myth-busting-css-animations-vs-javascript/

曾经某个时期,大多数开发者使用 jQuery 给浏览器中的元素添加动画。让这个淡化,让那个扩大,很简单。随着互动的项目越来越复杂,移动设备的大量增加,表现性能变得越来越重要。Flash 被抛弃,有天赋的动画开发者使用 HTML5 去实现过去从未实现的效果。他们需要更好的工具去开发复杂的动画序列并获得最好的性能。jQuery 并不能够做到。浏览器日渐成熟的同时也开始提供了一些解决方案。

最被广泛接受的方案是使用 CSS 动画(以及 Transitions)。几年中,它成为了业内的热门话题,在各种研讨会上,“硬件加速”和“移动端友好”之类的说法总是不绝于耳。基于 JavaScript 的动画总是被当做过时的甚至是“肮脏的”。但是真的是这样吗?

作为一个对动画和表现深深着迷的人,我如饥似渴地投入了 CSS 的怀抱,但当我开始发现了一些大问题后,我却没有深入研究进去。我被震惊了。

这篇文章用于揭示基于 CSS 的动画的一些重大缺陷,这样你可以避免碰到曾经困扰我的问题,同时也教会大家决定何时用 JS 动画以及何时用 CSS 动画。

缺少独立的 scale/rotation/position 控制

对元素的尺寸,旋转以及位置设置动画是非常常见的。在 CSS 里,这些设置都被塞进了transform属性当中,这样就不能够真正地独立控制它们。例如,你该如何用不同的时间和缓动函数去分别控制元素的rotationscale属性?可能这个元素会不停的震荡,并且你想去旋转它。这只有用 JavaScript 才能实现。

See the Pen Independent Transforms by GreenSock (@GreenSock) on CodePen.

在我看来,这是 CSS 动画一个很显著的问题,但是如果你只需要开发一些简单的动画效果,它们同时触发整个 Transform 状态的话,这也不是什么大问题。

表现性能

大多数比较都会拿 CSS 动画和 jQuery 相比,因为 jQuery 的使用非常普遍(就好像 JavaScript 和 jQuery 是同义词一样)。但是 jQuery 的动画性能很差也是众所周知的。较新的 GSAP 同样是基于 JavaScript 的,但是毫不夸张的说它的性能相比于 jQuery 提升了近 20 倍。因此,JavaScript 动画声名狼藉的部分原因我认为是 jQuery。

使用 CSS 动画的原因中,常常被提起的是“硬件加速”这个概念。听起来很高大上是吧?我们来把它分解成两个部分:

GPU 的使用

GPU 在执行类似控制像素点的移动和应用变换矩阵和透明等方面都做了优化,因此现代浏览器会试着把这方面的任务从 CPU 转交给 GPU 来完成。秘诀在于将应用动画的元素独立出来,建立一个自己的 GPU 层,因为只要一个层被创建,让 GPU 去移动那些像素点并把它们组合起来是很轻松的一件事。不同于以每秒 60 次的速度计算每个像素点的位置,GPU 可以把大量的像素以层的方式储存,然后我们就可以通过像“把那块像素向上移动 10 像素再向下移动 5 像素”这样的方式对像素进行操作了。

注解:GPU 是有图像存储空间限制的,因此将每个元素都转换为一个层是不合适的。一旦 GPU 的存储空间用完了,速度就会急剧降低。

通过 CSS 声明动画能够让浏览器决定哪个元素应该获得 GPU 层,并根据实际情况分配资源。很方便。

但是你知道你可以用 JavaScript 做到同样的事情吗?用一个 3D 特性的触发器(比如translate3d()或者matrix3d())来让浏览器为这个元素开辟一个 GPU 层。所以 GPU 加速不仅仅是为 CSS 动画准备的,JavaScript 动画一样可以受益!

另外记住,不是所有的 CSS 属性在 CSS 动画中都能够获得 GPU 的加速。实际上,大多数是不能的。变换(比如 scale,rotation, translation 和 skew)和透明效果是直接受益的。所以不要想当然认为你只要用了 CSS 动画,所有的效果都得到了 GPU 的帮助,那是不对的。

将计算转移给不同的线程

“硬件加速”的另一个方面是使用 CPU 的不同线程来进行和动画相关的计算。再一次的,理论上听起来很美但是它实际上和性能的开销无关,开发者往往会高估它带来的好处。

首先,只有与文档流无关的属性才能真正被移交给另一个线程。所以再一次的,变换和透明度是首要受益者。而转移线程的过程中也是有开销的。在大多数动画中,图像的渲染和文档的展现已经耗费了大多数的处理器资源(这还没有算上中间动画属性本身所耗费的资源),因此转移线程所带来的好处已经微乎其微了。比如,在一个动画中,98%的资源都用来计算图像的渲染和文档流的展现,只有2%的资源用来计算位置、旋转、透明度等属性,即使你将这部分的计算速度加快了十倍,整体带来的速度提升可能也只有1%而已。

性能比较

下面的压力测试创建了一定数量的图像元素(点),并且使用动画让它们从中点沿着随机方向以随机的延迟向边缘飞去,展现了一种飞舞的粒子效果。将粒子的数量提高,观察 jQuery,GSAP 和 Zepto 的比较效果。由于 Zepto 使用了 CSS 来展现所有的动画效果,它的性能应该最好是吗?

See the Pen Speed Test: GSAP vs CSS Transitions (Zepto) vs jQuery by GreenSock (@GreenSock) on CodePen.

结果证实了大多数网上的结论,CSS 动画明显比 jQuery 要快。但是,在我测试的大多数设备和浏览器上,GSAP 的性能甚至比 CSS 还要好(在某些情况下差距还很大,比如在 Microsoft Surface RT 上 GSAP 的速度比 Zepto 创建的 CSS 动画快上 5 倍,并且在 iPad 3 iOS 7 上 GSAP 的动画变换也比 CSS Transition要快)。

Animated properties Better w/JavaScript Better w/CSS
top, left, width, height Windows Surface RT, iPhone 5s (iOS7), iPad 3 (iOS 6), iPad 3 (iOS7), Samsung Galaxy Tab 2, Chrome, Firefox, Safari, Opera, Kindle Fire HD, IE11 (none)
transforms (translate/scale) Windows Surface RT, iPhone 5s (iOS7), iPad 3 (iOS7), Samsung Galaxy Tab 2, Firefox, Opera, IE11 iPad 3 (iOS6), Safari, Chrome

到底有多快?在测试的最初版本中,有每秒渲染帧数这一量化指标,但是很快就发现并没有一个准确的方式跨浏览器去测量 FPS 的值,尤其是 CSS 动画。并且特定的浏览器会得到令人误解的数字,所以我把它移除了。你可以进行间接的测量,比如逐渐提高粒子数量,切换不同的动画引擎,观察动画的渲染质量(移动平滑稳定,粒子分散度高等等)。毕竟,我们的目标是让动画看起来好看。

一些有趣的事情:

尽管经过优化的 JavaScript 动画经常和 CSS 动画一样快,甚至更快,但是 CSS 处理 3D 变换的速度还是更快,但这还跟当今浏览器处理 16 元素的矩阵的方式有关(强制将数字转换为连接的字符串,再转换为数字)。好在这种情况将会改变,在大多数实际的项目中,你并不会感觉到这种区别。

我鼓励你在自己的项目中通过自己的测试去找到性能最高的实现动画的方式。不要相信 CSS 动画性能一定高的说法,也不要让上文的测试影响你自己的应用中的结果。一定要自己测试,测试,再测试。

运行时的控制和事件

一些浏览器允许你在 CSS keyframes 动画中暂停,但最多也就是这样了。你不可能在动画中寻找一个特定的时间点,不可能在半路反转动画,不可能变换时间尺度,不可能在特定的位置添加回调函数或是将他们绑定在一大堆回放事件上[?]。JavaScript 提供了很棒的控制方法,请看下面的例子:

See the Pen Impossible with CSS: controls by GreenSock (@GreenSock) on CodePen.

现代动画大多看重交互性,因此从多种起始变量通过动画变换为结束状态就显得尤其有用(例如可能是基于用户点击鼠标的位置),或者改变正在运动中的元素,提前声明式的 CSS 动画就做不到这一点。

工作流

对用两个状态之间的简单切换(比如翻转或是展开菜单),CSS 变换是很好用的。对于连续变换状态的动画,你需要使用 CSS keyframes 动画来实现,它迫使你通过百分比的方式来声明动画,就像这样:

@keyframes myAnimation {
  0% {
    opacity: 0;
    transform: translate(0, 0);
  }
  30% {
    opacity: 1;
    transform: translate(0, 0);
  }
  60% {
    transform: translate(100px, 0);
  }
  100% {
    transform: translate(100px, 100px);
  }
}
#box {
   animation: myAnimation 2.75s;
}

但是动画进行的时候,你难道不觉得通过时间表示比使用百分比更好吗?就像“用 1 秒钟降低透明度,再用 0.75 秒向右滑动,再过 1 秒后向下掉落”。如果你用了几个小时通过百分比的方式实现后,客户又要求你将中间的步骤用时增加 3 秒呢?你需要重新计算所有的百分比了!

创建动画的过程中常常需要进行很多的试验,尤其是针对时间和缓动方式,这是seek()方法能派上用场的地方。想象你创建了一个 60 秒长度的动画然后需要对最后 5 秒进行处理,为了看到编辑后的效果,你每次都要先等上 55 秒钟的时间才行。你可以使用这个方法直接跳过前面的时间,最后再移除这个方法,这样可以节省大量的时间。

现在创建基于 canvas 的对象以及第三方库对象的动画的场景越来越多,而 CSS 动画又只能以 DOM 元素为目标。也就是说即使你花了大量的时间研究 CSS 动画,在那样的项目中也不会有用,最后你还是不得不更换工具。

以下是一些其他的 CSS 动画不能提供的工作流程相关的便利:

以下的任何效果你都无法通过 CSS 实现:

基于 CSS 的动画在 IE9 及之前版本的浏览器中都无效,我们大多都讨厌适配老版本的浏览器(尤其是 IE),但现实是我们的一些客户会要求那样的支持。

对许多浏览器来说,前缀是需要的,不过你可以利用预编译工具来避免手动书写它们。

结论

CSS 动画不好吗,当然不是。实际上,在实现一些简单的状态变换(比如翻转)并不要求对老版本浏览器兼容时 CSS 动画是非常方便的。3D 变换经常能有非常好的表现(iOS7是个例外)。而且对于喜欢把所有动画实现都放在 CSS 层的开发者来说 CSS 动画非常有吸引力。但是,基于 JavaScript 的动画灵活性更高,能更好的实现复杂的动画和大量的交互。并且和你听说的不一样,它能有和 CSS 动画一样,甚至更好的性能。

当和jQuery.animate()进行比较时,我能够理解为什么 CSS 动画会那么受欢迎了。谁不想要 10 倍的性能提升呢。但是很快就不用在 jQuery 和 CSS 中进行选择了,基于 JavaScript 的工具 GSAP 提供了全新的可能性,也消除了性能的差距。

这篇文章不是要说 GSAP 或是任何其他的库;而是要说 JavaScript 动画不应该背上坏名声。实际上,JavaScript 是搭建健壮的,灵活的动画系统的唯一选择。另外,我也想让大家看到 CSS 动画中令人失望的部分,这样,你可以对动画实现的方式进行更科学的选择。

Web Animation 标准能解决问题吗?

W3C 正在制定一份新的有关解决 CSS 动画和变换缺点的标准,提供更好的控制和更多的功能。从很多方面来说它确实有很大的进步,但它也是有缺陷的(其中的一些方面甚至是不可能的,它们会和现有的合法的 CSS 标准发生冲突,例如,独立的变换控制组件看起来是不可能 的)。但那是另一个话题了,我们可以看看事情会如何发展。毕竟还是有很多聪明人在为这份标准努力的。

ideazhao commented 10 years ago

看文留名,收益良多呀!~

Phinome commented 10 years ago

"尽管经过优化的 JavaScript 动画经常和 CSS 动画一样快,甚至更快,但是 CSS 处理 3D 变换的速度还是更快,但这还跟当今浏览器处理 16 元素的矩阵的方式有关(强制将数字转换为连接的字符串,再转换为数字)。好在这种情况将会改变,在大多数实际的项目中,你并不会感觉到这种区别。这段是什么意思?" 原文里没有这句话,我理解楼主的意思是这句话的展开意思不明晰,是吧?

classicemi commented 10 years ago

@Phinome 是的,这段话我是根据英文字面直接翻译的,但它所表示的意思我没有理解。

nunnly commented 10 years ago

火钳刘明,受教了

zhuxiaojian commented 10 years ago

在移动端一个很小的问题就有可能被放大十倍或是几十倍,性能对于移动端来说更加重要,animate已经避免使用了,很多时候会借助GPU来做一些事情,现在的手机都号称四核,不好好利用下就浪费了呢.

drawcall commented 9 years ago

很棒的文章 翻译的也很到位

rambo-panda commented 9 years ago

网上还有一段关于他们的比较: http://davidwalsh.name/css-js-animation 上述网址中主要推荐了 Velocity.js 库在动画方面的阐述。

个人总结: css3 动画可以启动GPU加速。 这是他的最强优势。以及您上面提到的css可能的缺陷。

 至少现在来讲,还无法做到css3来“统治”动画领域的地步(这个词不知道恰当不恰当啊哈哈)。

动画完美解决方案 绝对离不开css js的配合。 ps: 另外我也特讨厌把jquery 跟 原生的css来进行比较。

以上个人阐述!

potcode commented 9 years ago

翻译的很棒!

dongtianee commented 9 years ago

翻译的很好!我在博客中引用了这篇文章和翻译

实现了一个百度首页的彩蛋——CSS3 Animation简介