hxh

Polygon 的设计: SVG 动画的乐趣与益处

hxh · 2017-01-08翻译 · 800阅读 原文链接

Polygon 的 PlayStation 4 ReviewXbox One Review,是 Vox media 编辑部和产品部共同合作所达到的前所未有的水平。目的是创造一种极高的触控性能,以凸显我们的作者和视频团队的才能,同时也希望挑战 longform 的设计。

其中有很多经验教训,但最终的结果不言自明。

PS4 Animation

为什么选择 SVG?

起初,在评审设计稿的时候我们并没有考虑使用 SVG。但我们很快意识到,SVG 的矢量和路径描边功能,能够支持细腻的线图,它很符合我们的需求。而事实证明,它不仅能满足我们的审美需求,同时也能满足技术上的需求。

Polygon,是一个响应式的网站。然而,标准的图像格式,如‘jpg’,‘gif’,或者‘png’都不能根据用户的浏览器尺寸很好的进行缩放。另一方面,SVG,具有完美的响应性能:矢量能够随意缩放而不失真,SVG 的动画效果和操作能够根据其尺寸自动调节,而不需要任何额外的工作。

Xbox Animation

但在我们可以使用 SVG 之前,它们需要被创建出来。两个游戏机都没有容易得到的现成的 SVG 模型。需要从头开始设计。

从 Illustrator 导出 SVG

Polygon 的设计者,Tyson Whiting,根据两个游戏机的现实照片,精心描绘出路径线图。通过 Adobe Illustrator 创建 SVG 相对简单些,即使这样做有点枯燥。

第一次从 Illustrator 中导出测试用的描绘线图之后,发现存在几个问题需要解决:

  1. Tyson 在 Illustrator 中应用的颜色、填充、描边宽度,导出之后,都成了 SVG 元素的内联属性或者内联样式。

  2. SVG 标签与 Illustrator 对象(如“path”,“line”,“polygon”,“polyline”,“rectangle”)的实际对应关系并不明确,很多时候都不是我们想要的。

对于直线的导出没什么问题,但是其他的话,只能在 Illustrator 中手动处理导出的 SVG 文件了。

对于内联样式,转移到一个定义了页面中所有 SVG 样式的样式表中,然后我们通过反复试验,寻找出从 Illustrator 导出 SVG 时有固定标签的属性。

于是形成了一条流水线:Tyson,Polygon 的设计者,在 Illustrator 创建并导出 SVG;Ally Palanzi,Vox Media 的一名实习生,对导出的 SVG 手动处理,加入group 标签,并将标签注释清楚它们的作用。最后,我在 SVG 需要的地方添加上动画。

这流程不是最快的,但我们形成了一套良好的文档,也能清晰的使用 SVG。

可视化的管理 SVG 路径

在 SVG 上“绘画”,是通过控制 SVG path标签的两个属性形成的视觉差效果。

SVG 的path是独立的标签,连续的线条是根据坐标中的元素描绘出来的。它看起来像这样:

`<path></path>`

路径描边的动画效果,是基于 illusion 创建的图,通过调整 stroke-dasharrystroke-dashoffset 的值实现的。

stroke-dasharray 属性的值是以空格或者逗号分隔的列表,用于控制组成路径的 dash(线)和gap(空格)的长度。例如,当值设置为10 10path则由10像素的线条和10像素的空格交替描绘。根据这一思路,如果path的总长度为 10,那么“dash”的长度就等于path的长度。

确切的说,path 没有设置 stroke-dasharray和 将 stroke-dasharray 的 dash 值设置为 path 的总长,在视觉上没有差别。反之,也意味着 dash 后 gap 的长度也等于 path 的长度。

为什么这很重要?

第二个属性,stroke-dashoffset 用于控制 dashes 在 path 上的 偏移量。 这里,偏移量就是stroke-dasharray设置的虚线中第一个“dash”在路径上的起始位置。stroke-dashoffset的默认值是 1,也就是从起点开始。

stroke-dashoffset 设置的值为path的长度会是什么效果?

这意味着虚线从等于path长度处开始。换一种表达方式,stroke-dasharray定义的 dashes 之间的空格填充整个path。或者更简单的说,path不可见。

结合这两个属性,我们就可以控制在视觉上展示多少path

浏览器还是会将完整的path绘制并渲染出来。然而,由于定位,还有线和空格的长度,设置stroke-dasharry的值等于path的长度,在加上stroke-dashoffset值的变化,就能够创建出视觉上的描边动画。

SVG 路径描边动画

有许多方法能够利用以上两个属性生成动画效果,其中 CSS 过渡和基于 JavaScript 的间隔动画是最实用和支持度最广泛的。每一种方法都有它的优缺点,但我们选择 JavaScript 而不是 CSS 过渡的主要一个原因是:

时间的把握。

截止这篇文章,仍然没有一个可靠的方法去确定 CSS 过渡结束的具体的时间。可以使用等待时间为 transition 的过渡时间的setTimeout伪装监听,这样做的依据是相信 transition 的实际过渡时间和它所设置的时间一样。

在任何情况下,CSS 动画属性或者间隔动画的效果都类似于以下的伪代码:

for every path in an svg:
    store the results of getTotalLength()
    set stroke-dasharray equal to totalLength + " " + totalLength
    set stroke-dashoffset equal to totalLength
    animate()

animate用于改变stroke-dasharraystroke-dashoffset的值,具体内容会因实现方法的不同而有所不同。对于 CSS 过渡方法,animate会将transition设置为类似以下的值:

`stroke-dashoffset 2s ease-in-out`

然后,将每一个pathstroke-dashoffset设置至 0,浏览器会根据指定的 CSS 过渡值的变化,自动生成动画效果。

如果过渡的结束时间并不重要,前文提到方法是一个完美的。但是,如果你希望将动画串连在一起,或者指定动画结束立即执行某些事情,使用上述方法可能会比较麻烦。

为此你需要使用 JavaScript 手动设置 CSS 过渡的值渐变。

像这样的传统方法看起来可能像是 JavaScript 版的animate方法。

requestAnimationFrame

但是,这个方法有一个主要问题:draw方法对于浏览器的渲染周期一无所知,导致了不必要的调用。实际上,浏览器在做了许多它不需要做得事情,这样可能会降低浏览器的渲染速度,使得动画不流畅。

如何解决?

使用requestAnimationFrame。互联网上已经有大量的资源 解释了如何使用和为什么使用requestAnimationFrame,简单的说,如果你的JavaScript 动画是基于帧实现的 ,你应该使用它。它确保你所需要渲染的每一帧,都能够在浏览器渲染的时候被渲染。

使用 requestAnimationFrame,前文的代码现在看起来是这样的:

基于时间 vs 基于帧计数的动画

主要采用两种方法确定动画运行了多长时间:一个明确的时间,或者一个灵活的帧计数方法。两种方法各有优点。在 Polygon 的 reviews 中, SVG 动画主要使用了帧计数的方法,而不是设置一个明确的动画时间。

基于时间的动画就是上述使用动画路径的代码块。为动画运行提供一个精确的时间(上述例子中是 2000 毫秒)。而“帧数” 则根据运行所需的时间与总时间的比例计算出来的。

有一个很大的缺点是:在运行速度慢的电脑中,FPS 降低导致跳帧现象。

想象以下在 60 帧每秒的理想情况下,requestAnimationFrame每秒调用 60 次。看起来不错。也意味着 SVG 路径动画中stroke-dashoffset每秒改变 60 次——对于人眼,这样的频率足以使动画显得流畅。但是,如果 FPS 降低了——比如你的电脑硬盘突然开始抖动,所有的运行都变慢——requestAnimationFrame 突然变成每秒调用 15 次。因为我们将时间作为stroke-dashoffset值变化的决定因素,造成的视觉效果就是动画掉帧了。

动画出现跳帧现象。

另一种方法,就是使用时间之外的其它度量。在帧计数策略中,我们依赖于浏览器自身实际的 FPS,同样的动画效果,stroke-dashoffset 值的变化量是由帧数决定。以上的代码块可能看起来像是:

这样做,如果 FPS 降低了,视觉上是动画变慢了而不是跳帧。

在 Polygon’s PS4 和 Xbox 的 SVG 动画例子中,流畅度比运行所需时间更重要。

Metronome介绍

作为这两个 reviews 的开发工作的一部分,我们创建了一个内部的 JavaScript 库, 称为Metronome,我们希望能够尽快开源。它提供了一个简单的接口调用 requestAnimationFrame,并提供了多种 easing 方法。

敬请关注更多关于 Metronome 的消息!

Metronome github地址

相关文章