SVG动画实践

提醒:本文最后更新于 2703 天前,文中所描述的信息可能已发生改变,请谨慎使用。

SVG 是基于 XML 的矢量图形描述语言,可以近似理解成 HTML,所以能和 JS 以及 CSS 进行交互。 特别是 CSS,配合 CSS Animation 就能实现一些令人心旷神怡的动画,这实在是让人兴奋。下面我们就来看看 SVG 配合 CSS 实现动画的一些方法。

本文内容主要是我之前分享的文字版,若想看重点的话可以看之前的 Slide:

动画方式

动画的本质可以看做是元素从 A 状态过渡变化到 B 状态的过程。实现 SVG 动画主要有以下几种方式:

SMIL

SMIL 全称 Synchronized Multimedia Integration Language,该语言被 SVG 原生支持,主要使用标签来描述动画。据传,由于性能的问题以及 CSS Animation 越来越强大,Chrome 会在未来的版本中废弃对 SMIL 的支持。不过 MDN 上的声明却说 Chrome 推迟了废弃时间,也不知道是什么情况。总而言之,被 Chrome 这么一搞还是不推荐大家入门 SMIL 了。

Although Chrome 45 deprecated SMIL in favor of CSS animations and Web animations, the Chrome developers have since suspended that deprecation.

via: SVG animation with SMIL

如果已经使用了 SMIL 也不必特别担心,目前网上有 SMIL 的 JS Polyfill 方案,有需要的可以看看 https://github.com/ericwilligers/svg-animation 这个项目。

JavaScript

使用 JS 来操作 SVG 动画自不必多说,网上已经有很多现成的类库。例如老牌的 Snap.svg 以及 anime.js,都能让我们快速制作 SVG 动画。当然,除了这些类库,HTML 本身也有原生的 Web Animation 实现。使用 Web Animation 也能让我们方便快捷地制作动画。

CSS3

这里主要是使用 CSS3 动画三剑客(animation, transform, transition)来实现动画。优点不言自明,它比 JS 更加简单方便,关键帧的配合可以让其实现很多复杂的动画。

本文主要就从 CSS3 的角度来描述 SVG 动画的方法。

动画方法

动画可以拆成两部分,起始状态和终止状态是一部分,两态中间的过渡是另一部分。对过渡感兴趣的同学可以去看看月影老师的动画原理与实现的分享。而两态的变换,简单的来说可以分为变换和变形两种,即线性变换和非线性变换。

变换动画

平移变换

平移变换主要是利用 translate 等方法让元素移动位置达到的动画,只要善于使用,简单的移动也能做出不错的动效,例如下面的例子:

JS Bin on jsbin.com

这里有一个小技巧,就是在元素移动的起点和终点分别设置标记元素。使用 getClientRects() 方法获取标记元素的绝对位置,通过计算标记元素的位置差,换算成平移的距离。这样利用纯 CSS 就实现了位置移动的有趣动画。

旋转变换

旋转变换是使用 rotate 方法让元素进行旋转。使用时请注意:旋转圆心默认在 SVG 的左上角。可以通过设置 transform-origin 来修改圆心。本质上是将设置的圆心移动到坐标原点后再旋转再平移回来的一个效果,所以其实以下两种写法是等同的:

/** transform-origin **/
.rotate {
    transform: rotate(45deg);
    transform-origin: 100px 100px;
}

/** translate **/
.rotate {
    transform: translate(-100px, -100px) 
               rotate(45deg) 
               translate(100px, 100px);
}

由于旋转圆心如此重要,所以正如下面的这个例子,我们首要确认的是旋转圆心,剩下的就是简单的旋转角度的控制问题了。

JS Bin on jsbin.com

缩放变换

缩放变换则是使用 scale 方法让元素进行变大变小, 它的本质实际上是元素点 (x, y) 被乘以缩放系数了,即 (x * sacle, y * scale)。所以当设置了 transform: scale(2) 时,它相当于元素所有的坐标点变成 (2x, 2y) 了。

讲的这么详细主要是想让大家注意一个问题,当放大系数为负数的时候,例如 -1 那元素坐标点就会变成 (-x, -y),这就相当于元素针对横纵坐标分别做了一次镜像。对于一些需要实现镜像效果的元素这招就再简单不过了。

JS Bin on jsbin.com

斜歪变换

斜歪变换是使用 skew 方法使元素进行倾斜变形的方法。使用起来也是比较简单的,不过由于使用次数不是很多,本文这里就略过不表了。若有不清楚的同学可以上 MDN 或者 Google 搜索一下。

JS Bin on jsbin.com

矩阵变换

矩阵变换是所有变换方法的底层实现,上述变换方法都是矩阵变换某个方向的特例,完全可以使用矩阵变换实现。矩阵变换的使用方法就不多介绍了,不清楚的可以查下文档。一个 2D 的矩阵变换如下所示:

matrix

关于为何使用矩阵来进行矩阵变换计算,感兴趣的小伙伴可以参考《从矩阵与空间操作的关系理解CSS3的transform》,这篇文章浅显易懂的解释了使用矩阵表示的精妙之处。

JS Bin on jsbin.com

变形动画

变形动画即非线性变换动画,本文主要讲述描边,路径轨迹和路径变形三种变形动画。

描边动画

描边动画算是经典的 SVG 动画了,给人一种绘画的视觉效果,所以描边动画的名字因此得来。它主要是利用 Path 的描边长度 stroke-dasharray 配合描边起始位置偏移量 stroke-dashoffset 来实现的动画。首先设置 stroke-dasharray 等于路径长度,然后将 stroke-dashoffset 从路径长度减少到 0 ,就完成了一次描边。

可以看到,描边动画其实主要在于设置描边的长度,除了在 AI/Sketch 等软件中获取路径长度外,我们还可以使用 JS 的 getTotalLength() 方法获取。除了使用 CSS3 Animation 来实现动画外,我们也可以使用 JS 来绘制动画,网上有很多现成的类库。其中比较推荐大家使用 drawsvg 这款描边库,能简单快速地实现很多复杂的描边特效。

JS Bin on jsbin.com

路径轨迹动画

路径轨迹动画简单来说就是让元素沿着某条路径进行运动的动画。如果用其它方法实现这个效果的话可能会略复杂,但是用 SVG 就非常得心应手了,有多种方法能用来快速实现。使用原生的 SMIL <animateMotion> 标签,或者 CSS3 的 offset-path 属性,亦或是使用 JS 动态计算都是可以的。其中 SMIL 和 CSS3 的实现方法非常类似,这里主要说一下 CSS3 方法。

简单来说,设置 offset-path 属性为某一条 Path 路径,然后通过改变 offset-distance 的距离值,就可以实现元素沿着 offset-path 运动的动画。这种搭配方法让人不由得想到了描边动画。至于 offset-path 属性的前世今生,可以参考张鑫旭老师的《使用CSS offset-path让元素沿着不规则路径运动》

JS Bin on jsbin.com

路径变形动画

严格意义上,路径变形动画才真正算的上“变形”动画,它表示的是路径之间不规则变化的动画。从 A 路径变形成 B 路径,我们现在可以直接使用 CSS3 Animation 来实现,浏览器会自动帮我们做补间动画。当然我们也可以使用一些 JS 库例如 Snap.SVG 来制作。

不管是使用 CSS3 还是使用 Snap.SVG,都需要注意一个问题,那就是两条变形路径内的描点数需要保证一致,而且需要都是简单的描点,不能存在弧线等高级描点。这么实现的原因很好理解,能够极大减少补间动画的运算量,本质上可以归类为每个描点的平移动画。

但是这种限制对于开发者来说还是颇为麻烦的,所以很多类库就想要解决这个问题。其中比较著名的有 GreenSock,它们编写的 GSAP 插件据说能够对路径进行任意变形,从官网首页的动画也可窥见一二。另外一款插件是 SVG Morpheus,也能实现同样的效果,不过可能因为算法的问题,补间动画似乎并没有 GSAP 那么流畅。另外最近腾讯 AlloyTeam 发布了一款插件 Pasition ,用来实现路径过渡效果,使用起来也很方便,感兴趣的同学可以试一试!

JS Bin on jsbin.com

后记

本文只是涉猎了一些常用的变换方法以及一些基本的变形动画,SVG 还有很多例如遮罩、滤镜、填充动画方法,感兴趣的同学可以去了解下,都能实现非常有趣的动画特效。不过万变不离其宗,只要你富有想象力,能够设计出一些别出心裁的动效,即使只使用上文提及的一些简单的动画方法,我相信最终的动效也一定是非常有意思的!

学习资料

Avatar
怡红公子 擅长前端和 Node.js 服务端方向。热爱开源时常在 Github 上活跃,也是博客爱好者,喜欢将所学内容总结成文章分享给他人。

9 评论

rushmyessay Chrome61.0 Windows 10
2017-11-08 14:25:15 回复

真的很强大啊

玄玄 Chrome55.0 Windows 7
2017-09-26 06:04:16 回复

居然还在啊。
好久没更新我的博客了,点了很多以前保存的链接,大多都已经消失不见了。

d Edge15.15063 Windows 10
2017-09-08 09:01:53 回复

Edge炸了……

xqiushi Chrome59.0 Mac OS 10.12.6
2017-07-04 12:44:30 回复

强大呀

dont Chrome59.0 Windows 7
2017-06-30 07:32:11 回复

强大,最近闲来无事也在看svg,动画这部分,mdn感觉资料不全,有个不成熟的疑问:对于用css来完成svg的动画部分,涉及到像素px,会不会没SMIL直接在svg viewbox画布里的坐标系统来得简便些

公子 Chrome59.0 Mac OS 10.12.6
2017-06-30 12:31:59 回复

@dont 其实你在 CSS 中用 px 和在 svg 里面的单位是一样的,大小都一样,没有区别,CSS 外面如果有些不写单位也能生效的其实也是加了 px 来着,所以我觉得没啥问题。

韭菜韭菜 Firefox54.0 Windows 10
2017-06-28 08:43:27 回复

总结得太好了!大长见识!

美股行情 Firefox54.0 Windows 10
2017-07-19 03:52:15 回复

写得很好,很感激。已经收藏了。

美股指数 Firefox50.0 Windows 10
2017-08-31 10:57:52 回复

学到了很多~~~~~~~