整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

手把手教你轻松实现SVG动画

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

本文内容主要是我之前分享的文字版,若想看重点的话可以看之前的 Slide:https://ppt.baomitu.com/d/9037b180 也可以查看分享视频:http://cloud.live.360vcloud.net/theater/play?roomid=2079

1. 动画方式

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

1.1 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 这个项目。

1.2 JavaScript

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

1.3 CSS3

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

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

2. 动画方法

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

2.1 变换动画

2.1.1 平移变换

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

在线预览:https://code.h5jun.com/dac/4

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

2.1.2 旋转变换

旋转变换是使用 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);

}

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

在线预览:https://code.h5jun.com/bec/1

2.1.3 缩放变换

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

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

在线预览:https://code.h5jun.com/kul

2.1.4 斜歪变换

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

在线预览:https://code.h5jun.com/xaw/1

2.1.5 矩阵变换

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

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

在线预览:https://code.h5jun.com/dafa

2.2 变形动画

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

2.2.1 描边动画

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

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

在线预览:https://code.h5jun.com/nebu/1

2.2.2 路径轨迹动画

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

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

在线预览:https://code.h5jun.com/cora/1

2.2.3 路径变形动画

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

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

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

在线预览:https://code.h5jun.com/mugi/1

2.3 后记

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

学习资料

  • SVG 动画实现@CSS Conf 2016

  • SVG Tutorial

  • 张鑫旭的技术博客

  • w3cplus

  • SVG 开发与应用

  • https://segmentfault.com/a/1190000009378881

  • @优秀网页设计

  • codepen.io

源:众成翻译 译者:betsey

游戏的灵感

在使用过一段时间的SVG动画之后,我相当清楚如何利用它来制作动画片段或者网页布局。一些动画库平台,例如Greensock,和原生的CSS动画简直是绝配。于是我便打算深入地研究一下,看看我能否用这些来制作一款简单的游戏。就像一个精美的工艺品一样,好的游戏拥有许多细致的动画细节。有一天夜里,我的脑海中突然闪现了一个游戏的灵感,我马上起来,画了一些草图,之后给我的哥哥看--他是一个专业的网页设计师。我们立即开始着手设计,讨论了所有的细节动画后(试着画出来并且配以音效),我便开始进行游戏开发。

如何玩: 来回弹跳的球是可以改变颜色的。你必须随时观察球当前的颜色,并且在球和柱子相接触的一瞬间,确保两者的颜色是一样的。点击柱子可以改变它的颜色,单击变红,双击变黄,三击则变为紫色。

这里是游戏的完整版: http://codepen.io/gregh/full/yVLOyO

创作的过程

在开发这款游戏的过程中,我不断地遇到问题并重构代码。其中的一个最主要的问题就是,如何能让游戏在所有的设备和所有尺寸的显示器上都看起来不错。我使用我的Macbook Pro 开发,游戏的画面很棒。但是当到了 27" iMac screen 上时,整个画面就看起来特别小,当然在iPhone又会显得特别大。我真心地希望可以有一把适配所有设备的“万能钥匙”。经过了许多次的尝试之后,我清楚地意识到,传统的使用媒体查询的技术来做响应式设计是行不通的。

这篇文章并不是一个教程,因此我将不会逐行的解释我的代码。但是,我会展示给你一些十分酷的东西,你可以通过在CodePen上或者是在浏览器的调试工具里面修改参数进行测试。同样,我也会在相应的地方写出一些参考资料。在CodePen上,我尽可能多的写出代码注释,赶快去看看代码吧!

十分强大的GSAP让我理所当然地选择了它,而我选择在CodePen上面写码的原因是它内置了一个Babel编译器,这样我就可以在上面书写ES6的语法,你不知道Class和箭头函数有多好用! 关于ES6的特性介绍,你可以点击这里: https://github.com/DrkSephy/es6-cheatsheet

使用GreenSock制作动画

我下面假设你熟悉GSAP用法,但如果你不熟悉的话,你可以看下这个“讨厌番茄”的人的关于GSAP的教程。https://ihatetomatoes.net/get-greensock-101/

背景动画

几乎所有你能在背景上看到的东西都是用SVG制作的。每个波浪是一个独立的<div>,每一层的山峰也是一个<div>,甚至云也是<div>。当你在制作一个复杂的动画时,有一点是需要注意的。你完全可以使用一整个SVG当做背景,然后为这个SVG的子元素和路径来制作动画。Greensock允许我们这样做,你需要做的仅仅是为这些SVG的元素(比如说path,group等等)分配一些ID,然后用过ID选择到他们。但这样做的问题在于,在移动设备上,这些动画跑不动。所以你最好是把这些背景元素放在独立的<div>里面,然后为这些<div>添加动画,我实际上就是使用这些SVG的作为背景。

通过类似于上面的代码,我们就得到了一些简单的补间动画。这些动画可以将背景水平地移动54个像素(也就是背景的宽度),在这里我们想要背景匀速地移动,所以我们不需要缓动动画。当我们为每个背景设置不同的移动速度的时候,他们就有表现出了视觉差的效果,看上去很酷吧!

看到画面上漂浮着一些白色的小圆点了没?我创造了这些圆点并为他们设置了随机的位置和尺寸,接着我让他们做圆周运动。

这样他们就缓慢地进行圆周运动,但看上去却像随机运动一样。

柱子动画

每一个柱子里都有一些会动的小元素,这些小元素仅仅是由HTML和CSS制作出来的。使用SASS可以节省很多时间和代码量(通常情况下是这样的)。我通过创建形状的mixins来为这些柱子里面的小东西添加效果。如果我们看下bublble这个效果的代码,我们就会发现每个圆圈都使用了绝对定位并使用到了这个mixin。在CSS中创造三角形需要很多的代码,所以mixin就派上了大用场。

让我们看看红色的柱子,柱子里面包含着很多的气泡:

所以现在,如果想要在柱子里面创造一些气泡的话,我只需要调用这些mixin,设置气泡的大小,然后让把他们放在柱子的特定的位置上就好了:

当你需要通过边框来制作一些小的三角形的时候,你需要用大概20行的css代码来生成,所以使用mixin实在是太有必要了。

气泡的动画

我使用交错动画来做这个效果。这个动画可以操控柱子里面所有的气泡元素,并且可以让每个气泡有一个小小的延时,让他们不会同时开始运动。点击这个链接,你可以了解更多的关于交错动画的知识:http://greensock.com/docs/#/HTML5/GSAP/TweenMax/staggerFrom/

三角形的动画

在黄色的柱子里面,我使用到了旋转的效果。但是你可能注意到,有些旋转是围绕着X轴的,有些是围绕着Y轴进行运动的。我们在这里使用到了 cycle属性。

方块的动画

在制作方块的动画的时候,我们也使用到了相同的技术。因此,有一半的方块是从左向右移动的,而另外一般则做反方向的运动。

分数的动画

让我们点击重新开始,再看下这个动画吧.

我想要分数的动画有一种“Q弹”的感觉,于是我就写了几行代码来形成这个效果。

为了做出这种“触电”(或者说是“Q弹”)的效果,我们需要制作正确的缓动动画。如果你想要看看你可以使用什么样的缓动方程,看下 Greensock 缓动观察器:http://greensock.com/ease-visualizer 选择 Elastic 并调整配置参数,你就可以实时地看运动效果。

弹性盒子

这款游戏的第一个创新性就是使用了“弹性盒子”,如果要是不了解弹性盒子的话,你可以阅读下这篇优秀的文章: https://css-tricks.com/snippets/css/a-guide-to-flexbox/ 或者看下 Laracasts上的相关系列。一旦你开始使用了弹性盒子,你就再也离不开它。下面我们看下我的的主菜单界面和游戏界面吧。

开始游戏的容器CSS样式如下:


“flex-direction: column”表示在容器内部弹性元素的排列方向。 设置为Column,则元素从上到下依次排列。而默认的(row)则将元素从左到右排列。弹性盒子可以这只空白区域,我们可以设定我们是在元素的前面、后面或者周围放置这些空白区域。动手试下,感受下弹性盒子是多么好玩吧!因为我们设置成了“space-between”,所以 Top 被放在了顶部, How to Play被放在了底部,而Logo Holder则放在了中间,空白区域被填充在了这些元素之间。 Align-items: center, 定于交叉轴(这个游戏中代表水平轴)上元素的对齐方式,center表示居中对齐。其中 How to Play也是一个弹性盒子,它的代码如下:


它也是一个flex元素,我们如果去设定他的flex-direction, 它将使用默认值(row),将其中的三个弹性元素水平的排列。第一个和第三个元素的“flex”参数为1,因此他们将占据所有的空白空间,flex是一个相当高端的特性。同样的,你也可以通过在How to Play上设置 justify-content: space-around来达到同样的目的.我使用flex: 1 的原因是,我想让中间的一列排在屏幕的最中央。

下面我们看下游戏界面。这个界面也是一个弹性布局。界面有一个柱子容器和一个球的容器。我不想让球是绝对定位的,因为我通过css让球刚好坐在柱子上,这样即使我改变柱子的高度,我也不需要改动其他代码就可以让球恰好落到柱子上。因此我将容器的flex-direction设置为 column,正如我所愿,球容器刚好紧挨着柱子的顶部。justify-content: space-between让球的容器永远靠着屏幕的顶部,而柱子容器永远靠着屏幕的底部。 现在我们给球的容器如下的样式:


球的容器也是弹性布局,我们将flex方向设为column(这样Y轴成为了主轴,元素从上到下排列)。之后我们通过 justify-content: flex-end将球推到了容器的底部,因此,我们便得到了两个紧密排列的容器,并且第一个容器的内部元素被推到了该容器的最下面,这样我们便做到了让球坐在了柱子上的效果。

试想下,如果我们不使用弹性布局会怎么做?我们也许会使用到floats, width: 33.33333% ,position: absolute 和 bottom: 0,我的天啊! Flexbox让整个界面变得如此整洁有条理,写码简直爽翻了.

让游戏界面可缩放

做这个游戏,最重要的事情是让画面变得可缩放。看下这款游戏在不同尺寸的屏幕上达到了完美的效果!如同我说的,我仅仅是使用了 CSS transform, 这样做具有它独特的挑战性。假如游戏的默认大小是 1200x800px.吗,如果你的屏幕大小和这个不一样的话,你需要通过调节系数让游戏的容器变得大一些或者小一些,也就是做个计算"screenHeight/800\".当然,如果设备的高度大于宽度的时候(当我们的平板或者手机处于垂直模式的时候)我们也需要相应地缩放我们的屏幕,下面是计算缩放比例的代码 :

显然仅仅是这样做不能让视觉体验变得完美,所以我们需要在缩小我们的游戏界面的同时让它垂直水平居中。

因此,我们需要让整个游戏的容器以相同的尺寸放大,这样当缩放界面的时候,容器可以100%的占据屏幕的尺寸。如果我们将界面缩小到原始尺寸的一半的时候,我们需要让它的容器放大到原来的两倍大小,这样容器便可以充满整个屏幕。相反如果屏幕很大的时候,我们需要将界面变为原来的1.2倍,那么容器将相应的从原始尺寸缩小到 screenSize/1.2


后记

我希望你能喜欢这个游戏和我写的这篇文章,我也希望通过这篇文章,你可以收获一些新的东西并作出十分精彩的作品

我将持续不断地完善我的游戏,并相应地更新文章,增加新的玩法或者尝试些新的技术哦!

本文由众成翻译(zcfy.cc)的译者翻译完成,抢先阅读更多优质英文技术文章,欢迎访问众成翻译。

击右上方红色按钮关注“web秀”,让你真正秀起来

前言

通常我们说的 Web 动画,其实包含了以下三大类:

1、CSS3 动画

2、javascript 动画(canvas)

3、html 动画(SVG)

3 种动画各有优劣,实际应用中根据情况作出取舍,本文讨论的是我认为 SVG 中在实际项目中非常有应用价值 SVG 线条动画。

SVG 的历史和优势(W3C)

在 2003 年一月,SVG 1.1 被确立为 W3C 标准。 参与定义 SVG 的组织有:太阳微系统、Adobe、苹果公司、IBM 以及柯达。 与其他图像格式相比,使用 SVG 的优势在于:

1、SVG 可被非常多的工具读取和修改(比如记事本)

2、SVG 与 JPEG 和 GIF 图像比起来,尺寸更小,且可压缩性更强。

3、SVG 是可伸缩的

4、SVG 图像可在任何的分辨率下被高质量地打印

5、SVG 可在图像质量不下降的情况下被放大

6、SVG 图像中的文本是可选的,同时也是可搜索的(很适合制作地图)

7、SVG 可以与 Java 技术一起运行

8、SVG 是开放的标准

9、SVG 文件是纯粹的 XML

10、SVG 的主要竞争者是 Flash。

与 Flash 相比,SVG 最大的优势是与其他标准(比如 XSL 和 DOM)相兼容。而 Flash 则是未开源的私有技术。

SVG 是什么

可缩放矢量图形,即SVG,是W3C XML的分枝语言之一,用于标记可缩放的矢量图形。(摘自MDN)

上面代码中,先谈谈 svg 标签:

  • version: 表示 <svg> 的版本,目前只有 1.0,1.1 两种
  • xmlns:http://www.w3.org/2000/svg 固定值
  • xmlns:xlink:http://www.w3.org/1999/xlink 固定值
  • xml:space:preserve 固定值,上述三个值固定,表示命名空间,当数据单独存在svg文件内时,这3个值不能省略
  • class:就是我们熟悉的 class 类选择器
  • width | height: 定义 svg 画布的大小
  • viewbox: 定义了画布上可以显示的区域,当 viewBox 的大小和 svg 不同时,viewBox 在屏幕上的显示会缩放至 svg 同等大小

有了 svg 标签,我们就可以愉快的在内部添加 SVG 图形了

SVG 基本形状

SVG 线条动画基础入门知识

MDN Web 有基本形状的文档,建议去看看。包含矩形、圆形、椭圆、线条、多边形、折线等等。

好了,有了基本的了解,我们继续今天的话题,SVG 线条动画。

SVG 线条动画

先看看效果图,然后想想如果是你,该怎么实现这个效果了?

SVG 线条动画基础入门知识

ok,像以前一样,我们先来解析一下(按步骤实现):

1、svg画个按钮(基础形状-矩形)

2、矩形只保留下方底边

3、实现鼠标:hover事件 + 动画效果

svg画个按钮

<div class="button"> 
 <svg 
 viewBox="0 0 320 60" 
 version="1.1" 
 xmlns="http://www.w3.org/2000/svg" 
 > 
 <rect class="shape" height="60" width="320"></rect> 
 </svg> 
 <div class="hover-text">Web 秀</div> 
</div>

添加样式

.button { 
 position: absolute; 
 width: 320px; 
 height: 60px; 
 top: 50%; 
 left: 50%; 
 transform: translate(-50%, -50%); 
} 
.hover-text { 
 position: absolute; 
 line-height: 60px; 
 width: 320px; 
 top: 0; 
 color: #1199ff; 
 font-size: 28px; 
 text-align: center; 
 cursor: pointer; 
} 
.shape { 
 fill: transparent; 
 stroke-width: 4px; 
 stroke: #1199ff; 
}

SVG 线条动画基础入门知识

button垂直水平居中、shape透明填充,边框宽度4px,边框颜色#1199ff。

也许你会对fill、stroke-width等属性有点懵,下面看看他们的描述:

  • fill:类比 css 中的 background-color,给 svg 图形填充颜色;
  • stroke-width:类比 css 中的 border-width,给 svg 图形设定边框宽度;
  • stroke:类比 css 中的 border-color,给 svg 图形设定边框颜色;
  • stroke-linejoin | stroke-linecap:设定线段连接处的样式;
  • stroke-dasharray:值是一组数组,没数量上限,每个数字交替表示划线与间隔的宽度;
  • stroke-dashoffset:则是划线与间隔的偏移量

重点讲讲能够实现线条动画的关键属性 stroke-dasharray 。属性 stroke-dasharray 可控制用来描边的点划线的图案范式。

SVG 矩形只留底边

这里我们给按钮添加stroke-dasharray:

.shape { 
 ... 
 stroke-dasharray: 160 520; 
 stroke-dashoffset: -460; 
}

SVG 线条动画基础入门知识

SVG hover动画

.button:hover .hover-text { 
 transition: 0.5s; 
 color: pink; 
} 
 
.button:hover .shape { 
 -webkit-animation: draw 0.5s linear forwards; 
 animation: draw 0.5s linear forwards; 
} 
 
@keyframes draw { 
 0% { 
 stroke-dasharray: 160 520; 
 stroke-dashoffset: -460; 
 stroke-width: 4px; 
 } 
 100% { 
 stroke-dasharray: 760; 
 stroke-dashoffset: 0; 
 stroke-width: 2px; 
 stroke: pink; 
 } 
}

hover时,改变文字颜色,利用stroke-dasharray和stroke-dashoffset实现动画效果。

后续文章将会详述非规则图形,如何使用 PS + AI 生成 path 路径,实现 SVG 动画,敬请期待。

公告

喜欢小编的点击关注,了解更多知识!

源码地址和源文件下载请点击下方“了解更多”