X.d 笔记

小Web,大世界

0%

SVG简单入门五:动画、裁切及蒙版

动画

与用户合理自然的交互,肯定是少不了动画的存在的,好的产品就是需要遵循人类的习惯,把数据以合理的方式一步一步展现出来,给与用户顺畅、自然的体验。

动画的原理

其实SVG的动画还是很丰富的,但这里只罗列一些基础。主要原因:

  1. 我目前实现SVG动画主要是靠d3,而不是SVG原生动画。
  2. SVG原生动画与d3动画并不冲突,可以结合起来一起使用,各司其职。
  3. 如果大部分动画交给d3,那SVG的原生动效只需要了解即可,使用时Google参考资料就行。
  4. 我想传达一些编程的思想,编程时要靠自己的判断技术的优缺点和适用性。

原理上来讲很简单,做前端的动画很多都是这种方式,比如这样一个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 。有兴趣可以参考。

高阶动画

通常我们做比较高级的动画时,就不是做个过渡比较简单了。比如:

  1. 为动画指定关键的时间点。
  2. 一个动画的运动结果做为另一个动画的触发条件。
  3. 在动画的过程中,进行各种交互。
  4. 延时、倒退、重复,反向,等等都有不同的算法。

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里定义标签为 ,通常放在 里面,包含一个ID,定义后通过 css的 clip-path:url(#clipId) 进行引入。

里面需要把裁切的图像形状作为子元素定义进去,可以是基础图形标签或是标签,另外裁切通常需要配合 来一起使用,即使用 use = 裁切路径(clip-path) + 被裁切的对象(xlink:href)

利用好裁切,也可心搞一些炫酷的事情,比如可以很容易的将一个图形的各个部分拆开显示在不同的地方,分别进行不同的组装进行展示。

基础的裁切代码:

<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里面,蒙版是 定义方法和较像,但可以直接使用在元素上,不需要使用use来进行内容与路径的组合,而且将mask直接运用于图像。

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),所以没做详细说明。