动画
与用户合理自然的交互,肯定是少不了动画的存在的,好的产品就是需要遵循人类的习惯,把数据以合理的方式一步一步展现出来,给与用户顺畅、自然的体验。
动画的原理
其实SVG的动画还是很丰富的,但这里只罗列一些基础。主要原因:
- 我目前实现SVG动画主要是靠d3,而不是SVG原生动画。
- SVG原生动画与d3动画并不冲突,可以结合起来一起使用,各司其职。
- 如果大部分动画交给d3,那SVG的原生动效只需要了解即可,使用时Google参考资料就行。
- 我想传达一些编程的思想,编程时要靠自己的判断技术的优缺点和适用性。
原理上来讲很简单,做前端的动画很多都是这种方式,比如这样一个SVG:
<rect x="0" y="0" width="0" height="200" />
因为宽度是0,什么都看不到,如果我在一定的时间里面将 width
从0 变到 1000,那就可以看到宽度慢慢加长的一个变化了,如果频率够高的话,人类的眼睛就几乎感觉不到这个过程中间有停顿了,所以就称为动画。一般来说一秒60帧,就是相当流畅了。
过渡算法
首先要设定时间和帧数:比如这个宽度在2秒钟的时间从0到1000,按每秒50帧(也是很流畅的)。那么2秒钟,就有100帧,相当于每20毫秒,这这个矩形的宽度会进行一次变化!
如果按照我们最常匀速变化,那相当于每20毫秒,宽度就会增加10,到第2000毫秒(2秒)结束时,宽度刚好到达1000。
那么,我们就称这个匀速计算每个时间点宽度的算法是过渡算法。这个匀速过度也称为线性过渡。
过渡的算法还有很多种,实际上就是通过对过渡过程中的快慢的控制,达到更好更炫的效果。常用的过渡算法在SVG, D3,还有各种工具包里面都有内置。个人好几年前也自己写的玩过: https://runjs.cn/code/fswjwdu7 。有兴趣可以参考。
高阶动画
通常我们做比较高级的动画时,就不是做个过渡比较简单了。比如:
- 为动画指定关键的时间点。
- 一个动画的运动结果做为另一个动画的触发条件。
- 在动画的过程中,进行各种交互。
- 延时、倒退、重复,反向,等等都有不同的算法。
SVG动画
实际上,SVG动画是基于一个称为 SMIL
的规制定的。
先看一下上面DEMO的SVG动画的实现,仅需要在元素内部加入 animate
标签即可
<rect x="0" y="0" width="0" height="200">
<animate
attributeName="width"
attributeType="XML"
from="0"
to="1000"
begin="0s"
dur="2s"
fill="freeze"
/>
</rect>
animate 标签的主要属性如下:
attributeName | 属性名称 | |
---|---|---|
attributeType | 属性类型 | auto |
from,to | 开始结束值 | |
begin,dur | 开始时间、持续时间 | |
fill | 结束后的动作 | freeze |
(请原谅我的一笔带过)关于更加详细的SVG的动画说明:可以移步: 超级强大的SVG SMIL animation动画详解
D3动画
关于D3的基本使用,请参考 SVG之七:D3入门 。在这里,我先说下SVG的动画在D3里面的实现。
在d3里面,使用动画变得简单了很多,对于上面的功能,代码如下:
d3.select('rect') //选中矩形
.transition() //开始动画
.duration(2000) //设定时间
.ease("linear") //过渡算法
.attr('width', 1000); //改变宽度
除了代码行数比SVG里面的少之外,它并没有使用SVG增加DOM节点的方式来定义动画,而是在JavaScript里面进行,所以灵活性要比SVG动画要强很多。
当然之所以推荐D3用来实现动画,是因为D3具备了更强大的API及算法,不仅可以变换属性,CSS,还可以支持各种style,text,自定义插值等等,和自身提供的比例尺API相结合,非常强大。
实际上SVG动画也有它的优点,比如说可以支持PATH运动等,请根据适用性来选择具体的实现方式。
裁切
在一些网站,上传一个正方形的头像后,会展示成一个图形,或是有圆角的方块来显示。而裁切就比类似,它只保留了图像的一部分后,把多余的内容丢掉。
在SVG里定义标签为 clip-path:url(#clipId)
进行引入。
在
利用好裁切,也可心搞一些炫酷的事情,比如可以很容易的将一个图形的各个部分拆开显示在不同的地方,分别进行不同的组装进行展示。
基础的裁切代码:
<defs>
<!-- 把对象中间的圆形剪出来 -->
<clipPath id="clip" clipPathUnits="objectBoundingBox">
<circle cx="0.5" cy="0.5" r="0.5">
</clipPath>
<symbol id="symbol">
<rect x="0" y="0" width="100" height="100" fill="#999"/>
</symbol>
</defs>
<use xlink:href="symbol" style="clip-path:url(#clip)"/>
蒙版
与裁切不同,裁切是把多余的切掉,保留选中的部分,而蒙版则是把选中的部分蒙住,多余的部分不变。
在SVG里面,蒙版是
mask通常使用半透明效果,使用一些颜色结合而做出比较不错的特效,比如:
<svg width="400" height="300">
<defs>
<linearGradient id='white2black'>
<stop offset="0" stop-color="white"></stop>
<stop offset="100%" stop-color="black"></stop>
</linearGradient>
<mask id="small-rect">
<rect x="0" y="0" width="400" height="300" fill="url(#white2black)"></rect>
</mask>
</defs>
<rect id="back" x="0" y="0" width="400" height="300" fill="#d4fcff"></rect>
<rect id="front" x="0" y="0" width="400" height="300" fill="#fcd3db" mask="url(#small-rect)"></rect>
小结
动画是一个非常重要的概念,是用户体验极其重要的一部分,动画的原理简单,但要做出好的动画,需要依赖于产品水平的发挥。
而裁切和蒙板,使用频率没那么高,加上自身API非常固定(主要是依赖于path),所以没做详细说明。