- 原文作者:Anand Sharma
- 译文出自:掘金翻译计划
- 译者:王子建
- 校对者:Scarecrow,Gocy
去年我们发布了 Gyroscope 以来,许多人问过我们做动画用的什么 JavaScript 库,我们也曾想过将它公布于众,但实际上那并不是奥妙所在。
我们不想让大伙儿觉得自己需要依赖特别黑魔法的 JavaScript 插件才能解决问题。大部分时候,我们都只要对最新的浏览器和 GPU 的性能和 CSS3 加以利用就够了。
其实并没有什么绚丽动画的武功秘籍,唯一的办法就是花大量时间测试和优化。但是,在经过多年的试验和浏览器性能的极限考验,我们发现了一些设计和编码的原则可以有效地提升动画表现。这些技巧能够使你的页面流畅,并且能够运行在流行的台式和移动设备的浏览器上,最重要的一点,它们还非常易于维护。
技术手段和实现方式可能因人而异,但是通用性的原则几乎能无所不包。
在互联网发明之前,动画就已经所处可见了,可能你需要穷尽毕生之力才能学会如何将动画做得绚丽辉煌。然而,在互联网中实现动画效果自有其独特的限制和挑战。
为了实现流畅的 60 帧的动画效果,每一帧都需要在 16 毫秒内完成渲染!时间很短,所以我们需要找到最高效的方法去渲染每一帧内容,从而实现流畅的表现。
在网站上实现动画效果的方式多种多样。比如,在互联网出现之前随处可见的电影胶片,它利用手绘的渐变的胶片,每秒钟播放多帧来实现动画的错觉。
Twitter 在最近的心形动画中就利用了这种方法,通过胶片绘出一个转动的精灵。
这个效果也可以通过许多独立的小元素动画来实现,或者用 SVG 实现,但是那样会过于复杂,并且可能不会这么流畅。
许多时候,你会想要使用 CSS 切换属性来自动实现元素改变的动画效果,这种技术被称作“tweening”—因其是在两个不同的属性值之间切换(译者注:tweening 来自 transitioning be_tween_ two different values)。它的好处是可以非常简单地取消或者替换掉而不用重新构造逻辑内容,这是完美的一劳永逸式的动画,像介绍序言等,或者如鼠标悬停等简单的交互。
更多资料: All you need to know about CSS Transitions
其他时候,基于关键帧的 CSS 动画属性会非常适合不断变化的背景元素。举个例子,陀螺仪中的圆环按会照预设持续转动,还有其他能够利用 CSS 动画的类型比如齿轮。
为了免于后顾之忧,希望以下这些建议能极大地提高你的动画效果:
#1
即便你觉得可行,那也别冲动!
动画中百分之八十的优化会用到这项基本原则,即使是在移动端也一样。你或许以前听过这个原则,这不是我提出来的,但是很少有人去遵守。这跟“管住嘴迈开腿”一样,建议很好却也最容易被忽略。
对已经习惯了这种思路的人来说这非常简单,但是对那些习惯用传统的 CSS 属性去做动画的人来说,这会是一次质的飞跃。
比如,你想让某个元素小,你可以使用 transform:scale(),而不是改变宽度;如果你想移动它,你可以使用简单的 transform:translateX 或者 transform:translateY,从而替代乱糟糟的外补白(margin)或者内补白(padding) — 那些需要重建每一帧的页面布局。
对人类来说,改变宽度、外补白或者其他属性不是什么大事 — 甚至因为简单会更让人喜欢这么做 — 但是对电脑来说,这事儿就像天塌了一样,甚至比这更糟糕。
浏览器投入了九牛二虎之力来优化这些操作,切换属性(transform)真的非常容易且高效,并且能够充分利用显卡,并且不用重新渲染元素。
第一次加载页面的时候,你可能会觉得抓狂 — 处理所有圆角、引入图像、给一切添加阴影,如果你毫不在乎那么甚至可以再做一个动态羽化。如果这种情况只会发生一次,多一些计算时间也没关系。但是一旦内容渲染完成了,你绝对不会再想要重新加载!
更多内容: Moving elements with translate (Paul Irish)
#2
使用 pointer-events 属性:仅仅利用透明度隐藏元素
或许会有跨浏览器的警示,但是如果你只是面向 webkit 和其他流行的浏览器,它将会让你如虎添翼。
很久以前,动画效果必须由 jQuery 的 animate() 方法来处理,许多复杂的淡入淡出效果的处理是通过 display 的属性值切换实现的。太早显示,那么动画还没完成,但是太晚的话就会在页面上显示一片空白,总是需要回调函数去给执行完的动画擦屁股。
CSS 中的 pointer-events 属性(尽管已经存在很长时间,但是不经常使用)只是让元素失去了点击和交互的响应,就好像它们不存在一样。它能通过 CSS 控制显示或隐藏,不会打断动画也不会影响页面的渲染或可见性。
除了将 opacity 设置为零,它和将 display 设置为 none 具有相同的效果,但是不会触发新的渲染机制。需要隐藏元素的时候,我会将它的 opacity 设置为 0 并将 pointer-events 设置为 off,然后就任由其自生自灭啦。
这样做尤其适用于绝对定位的元素,因为你能够自信满满地说他们绝对不会影响到页面中的其他元素。
它有时也会剑走偏锋,因为动画的时机并不总那么完美 — 比如一个元素在不可见状态下仍然可以点击或者覆盖了其他内容,或者只有当元素淡入显示完全的时候才可以点击,但是不要灰心,会有办法解决的。(下文会提到解决办法,译者注)
#3
用动作编排加以替代
单一的动画会很流畅,但是和其他许多动画一起也许就完全乱套了。编写一个流畅的全员动画的例子很简单,但当数量级上升到整个网站时性能就很难维持了。因此,合理安排好每个元素非常重要。
你需要将所有的时间节点安排好,来避免所有的动画内容同时开始或进行。典型的例子,2 或 3 个动画同时进行可能不会出现卡慢的现象,尤其是在它们开始的时间略有不同的情况下。但是超过这个数量,动画就可能发生滞缓。
理解动作编排这个概念非常重要,除非你的页面真的只有一个元素。它貌似是舞蹈领域的东西,但是在动画界它同样的重要。每个内容都要在合适的方向和时机出现,即使它们相互分离,但是它们要给人一种按部就班的感觉。
谷歌的 material design 有几点关于动作编排的有趣建议,虽然这并不是实现目标的不二法门,但总有一些是你应该去考虑和尝试的。
更多内容: Google Material Design · Motion
#4
动画的编排非常重要,同时也会做大量的试验和测试才能恰如其分。然而,动画编排的代码并不会非常复杂。
我通常会改变一个父元素(通常是 body)的 class 值来触发一系列的改变,这些改变有着各不相同的切换延时以便能够适时展现。单从代码来看,你只需要关心状态的变化,而不用担心一堆时间节点的维持。
Gyroscope Chrome Extension 的动画
交错安排一系列的元素是动画编排的一种简单易行的方法,这种方法很有效,因为它在性能良好的同时还好看—但请记住你本想让几个动画同时发生的。你想把这些动画分布开来,让每个都表现地流畅,而不是一下子太多动画从而显得特别慢。适当部分的重叠会看起来连续流畅而不是链式的单独动画。
有一些很简单的技巧来错开你的元素—尤其是其中有非常多的内容。如果页面中有小于 10 项内容,或者元素数量可预估(比如静态页面),我通常会在 CSS 中指定特定的值。这是最简单易行的方法了。
一个简单的 SASS 循环
对更多的内容或者动态内容来说,可以在循环中动态地给每项内容添加时间节点。
一个简单的 JavaScript 循环
有两个典型的变量:基本延时和各个项目的延时。它很难协调,但你一旦找到正确的值,效果将会非常完美。
#5
过后再加快动画的速度
动画设计中,时间节点就是一切。20% 的工作是用来实现效果,剩下的 80% 使用来寻找合适的参数和持续时间来让一切在同时发生时显得流畅。
尤其是在编排多个动画的时候,为了达到高性能和高共同性,观察动画的慢动作会让一切工作变得非常容易。
无论你用的是 JavaScript 还是 CSS 预处理器比如 SASS(我们非常喜欢它),都需要简单地做一些额外的计算并且需要声明一些有用的变量。
你必须确保它能够非常容易地尝试不同的速度或时间节点。举个例子,如果一个动画效果在 1/10 的速度下还表现地结结巴巴,那么可能会有一些非常基础的错误。如果在放慢 50 倍的速率下表现流畅,假以时日定能找到运行流畅的最大速度。或许正常速度下 5 毫秒的差池很难被注意到,但是放慢速度,它就变得非常明显了。
尤其是做非常复杂的动画分析,或者解决非常棘手的性能瓶颈,慢动作查看元素会非常的有用。
重要的一点就是,在慢动作下你会将非常多的细节优化地完美,当动画加速之后它将会给人完美无瑕的感觉。尽管这些都显得微不足道,但是用户会注意到动画效果的流畅和细节的。
只有 OS X 才有的功能—如果你 shift + 点击最小化按钮或者一个应用图标,你将会看见它在缓慢移动。基于这一点,我们甚至在陀螺仪上实现了这个功能,当你按下 shift 键的时候将会激活慢动作模式。
#6
有时候不同的视角能够帮助你对事物有更加清楚的认识,而录像则是一种很好的方法。
有的人会用 AE 做视频然后放到网站上,而我恰恰相反,我总是尝试将网站界面录制成很棒的视频。
发布视频其实门槛很高的。有一天我对做出来的东西感到非常激动,想记录下来和朋友们分享。
然而,当看第二遍的时候,我发现了一些瑕疵,时间节点设置得不那么恰当,并且出现了一个延迟尖峰。这让我有点打退堂鼓了,我发现还有很多的内容需要优化,所以我不能就这么把视频发送给朋友。
在使用过程中这些瑕疵都很容易被掩盖,但是在视频中一次次地观看慢动作的动画能够让一切问题都暴露地非常明显。
有人会说拍摄出来和看起来的效果并不完全相同,但也许它变更加精确了呢。
这已经成为我工作中很重要的一部分,我会观看慢动作的视频并且修改任何我觉得不妥的地方。其实也可以很容易地将这类问题归咎于浏览器性能差,但是再多优化一点多测试一点,这些问题就能够得到解决。
等到你在视频中不会发现非常尴尬的延迟尖峰,并且感觉视频挺好的可以晒出来了,这个时候你的页面就可以发布了。
#7
你应该预加载或者延迟处理非常大的 HTTP 请求
图片便是其中一个元凶,无论是几个大图片(大的背景图)或者非常多的小图(五十个头像),或者非常多的内容(一个从头到尾有很多图片的长页面)。
页面首次加载的时候,许多的东西会被初始化并下载。其中内容解析、广告和其他第三方脚本会使性能变得更糟糕。有时候,将动画效果在页面加载后延迟零点几秒将会对性能有很大的提升。
如果没有必要的话,不要过度优化动画延迟,一个复杂的页面要求非常精确的延迟和时间节点才能运行流畅。通常你会想要在开始的时候加载尽可能少的数据,当主要内容和介绍动画完成之后再继续加载其他的内容。
一个有很多数据的页面,需要深思熟虑地加载所有内容。一个在静态页面中表现良好的动画效果也许就会在实时数据的加载中变得缓慢。如果有些内容仿佛应该生效但却没有,或者不能一如既往地流畅表现,我建议检查一下网络活动,确认一下你是否也在同时处理其他的内容。
#8
貌似是个好主意,其实不然
基于滚动的动画在前些年一段时间非常火爆,尤其是涉及视差或者其他特效的内容里。它们的设计模式是好是坏仍有待考证,但是在技术上有着良莠不齐的实现方法。
基于滚动的动画中有一种非常流行的处理方式,即将滚动一定距离作为事件处理同时触发动画内容。除非你对自己的行为了如指掌,否则我会建议不要使用这种方式,因为它真的很容易出错并且很难维护。
更糟糕的情况是自定义滚动条功能,而不用默认的功能—又名 scrolljacking 。请不要这么想不开。
在这十项准则中,这项尤其适用于移动开发,另外可能也是理想用户体验的好的实践。
如果你确实要求独特的体验并且你希望它基于滚动或者其他的特殊事件,我建议创建一个快速原型来实现,而不是费力不讨好地去设计事件形式。
#9
大多数的网站都是在电脑上搭建的,并且最常用本机做测试。因此,移动端体验和动画性能就被次要考虑了。一些技术(比如 canvas)或者动画技术可能在移动端表现地并不好。
然而,如果代码写得好优化也到位(参考规则 #1),移动端的体验甚至比电脑更加流畅。移动端的优化是一项非常棘手的事情,但是新的 iPhone 比手提电脑更快!如果你采用了前几项建议,你将会得到一个非常棒的移动端表现。
移动端访问网站将会变得非常非常的重要。我建议你专门拿一个星期的时间认真地用手机查看你的网站,这或许有些极端,你可能会感觉像是在接受惩罚而被迫使用移动端版本,但是你应该调整好心态。
不断优化设计和提高性能,直到网站在移动端的表现和在电脑上一样优美和方便。
如果你坚持一周都用移动端来访问网站,你将会得到一个比电脑上更优化体验更好的网站。即使在使用过程中遇到非常恼人的事情也是值得的,那意味着这些问题将在你的用户体验到之前就被解决掉了!
#10
不同屏幕尺寸、分辨率,或者有着各种样式的设备
除了移动端和电脑之外还有很多因素能够对性能产生极大的影响,比如是否是 "retina" 屏幕、窗口的分辨率、硬件的老旧程度等等。
即使 Chorme 和 Safari 都是基于 Webkit 的浏览器并且有着相似的语法,但是他们也有各自的特点。每一次 Chrome 升级都会修复一些问题同时也会引入新的 bug,所以你必须时刻保持警惕。
当然,你不会只想着搭建一个对于所有浏览器放之四海而皆准的网站,所以寻找一个灵活的方法以便于你能够增加或者移除一些功能是非常有用的。
我通常会交替在较小的 MacBook Air 和大屏的 iMac 中使用网站,每次都会暴露出新的问题然后再修复 — 尤其是动画性能方面的问题,有时候也会有全局设计的题、信息密度、可读性的问题等等。
Media queries 是一款非常强大的工具,它典型的用处是定位由于高度或者宽度造成的样式差异,但是它同样能够用来根据分辨率添加目标内容或者其他属性。另外,识别系统和设备类型的功能也是非常有用的,因为移动设备的性能特征和电脑还是有很大区别的。