最近看到篇文章很有同感,很多时候想学习一些东西,会被一堆堆的代码所困扰,很难去读懂和理解。而用一个个小Demo的形式了解起来就更为直观,产生兴趣之后学起东西就自然相对轻松。

Demo1:创建基本形状

SVG 预定义了7个基本元素:circle 创建圆形rect 创建矩形ellipse 创建椭圆line 创建线条polyline 创建折线polygon 创建多边形path 创建路径
其中 path 可进行填充、勾划(沿着路径绘制直线),也可用作剪切路径(其他形状的图样),是所有绘图元素中最复杂的。

Demo1

<!-- 圆形 Circle -->
<svg width="200" height="200">
  <circle cx="100" cy="100" r="50" fill="#29ABe2" />
<!-- 矩形 Rectangle -->
</svg>
<svg width="200" height="200">
  <rect x="50" y="50" width="100" height="100" = fill="#29ABe2" />
<!-- 椭圆 Ellipse -->
</svg>
<svg width="200" height="200">
  <ellipse cx="100" cy="100" rx="50" ry="25" fill="#29ABe2" />
<!-- 线条 Line -->
</svg>
<svg width="200" height="200">
  <line x1="50" y1="50" x2="150" y2="150" stroke="#29ABe2" stroke-width="8" />
<!-- 折线 Polyline -->
</svg>
<svg width="200" height="200">
  <polyline points="30,50 70,50 70,90 110,90 110,130 150,130 150,170" fill="white" stroke="#29ABe2" stroke-width="6" />
<!-- 多边形 Polygon -->
</svg>
<svg width="200" height="200">
  <polygon points="100 50,65 150,150 90,50 90,135 150" fill="#29ABe2" />
<!-- 路径 Path -->
</svg>
<svg width="200" height="200">
  <path d="M20,100 Q40,50 100,100 T180,100" fill="none" stroke="#29ABe2" stroke-width="4" />
</svg>
  1. 属性说明:
    fill 属性:表示填充颜色
    fill-opacity 属性:填充颜色透明度(合法的范围是:0 - 1)
    stroke 属性:表示描边颜色(是 SVG 中有个比较重要的属性分支,下文中会有详细说明)
    stroke-width 属性:表示描边的粗细
    stroke-opacity 属性:表示描边透明度(合法的范围是:0 - 1)

  2. <circle>标签:
    cx 属性:圆点的 x 坐标
    cy 属性:圆点的 y 坐标(如果省略 cx 和 cy,圆的中心会被设置为 (0, 0))
    r 属性:圆的半径

    <rect>标签:
    x 属性:矩形的左侧位置(例如,x="0" 表示矩形到浏览器窗口左侧的距离是 0px)
    y 属性:矩形的顶端位置(例如,y="0" 表示矩形到浏览器窗口顶端的距离是 0px)

    <ellipse>标签:
    cx 属性:表示圆点的 x 坐标
    cy 属性:表示圆点的 y 坐标
    rx 属性:表示水平半径
    ry 属性:表示垂直半径

    <line>标签:
    x1 属性:x 轴开始坐标
    y1 属性:y 轴开始坐标
    x2 属性:x 轴结束坐标
    y2 属性:y 轴结束坐标

    <polyline>标签:
    points 属性:定义多边形每个角的 x 和 y 坐标

    <polygon>标签:
    <polyline> 标签

    <path>标签:
    M = 移动到;moveto(M X,Y) (移动到)
    L = 连线到;lineto(L X,Y)
    H = 水平链接到;horizontal lineto(H X)
    V = 垂直链接到;vertical lineto(V Y)
    C = 使用曲线连接到;curveto(C X1,Y1,X2,Y2,ENDX,ENDY)
    S = 使用平滑曲线连接到;smooth curveto(S X2,Y2,ENDX,ENDY)
    Q = 使用二次贝塞尔曲线连接到;quadratic Belzier curve(Q X,Y,ENDX,ENDY)
    T = 使用平滑的二次贝塞尔曲线连接到;smooth quadratic Belzier curveto(T ENDX,ENDY)
    A = 使用椭圆曲线连接到;elliptical Arc(A RX,RY,XROTATION,FLAG1,FLAG2,X,Y)
    Z = 将路径封闭到;closepath()

Demo2:实现一个简单的动画效果

在对基本元素有了一些了解后,我们首先已圆形为例,给元素添加动画效果。SVG动画基于SMIL(Synchronized Multimedia Integration Language - 同步多媒体集成语言)来实现。

Demo2

<svg width="400" height="400">
  <circle cx="200" cy="200" r="50" fill="#29ABe2" >
    <animate attributeName="r" attributeType="XML" from="50" to="80" begin="0s" dur="2s"/>
  </circle>
</svg>
  1. 根据SMIL动画规范,有以下4个基本的动画元素:
    <animate>:定义动画的实时属性(例如:持续时间、属性和属性值)
    <set>:在指定的时间之后执行另外的动画效果,通常用来修改非数字类型属性所产生的动画
    <animateMotion>:让元素沿着特定的路径运动
    <animateColor>:在指定的时间内修改元素的颜色(SVG 2.0 中已被移除,使用 <animate> 也能实现相同效果)

  2. attributeName 属性:
    动画属性的名称。可以是元素直接暴露的属性,可以是CSS属性。

  3. attributeType 属性:
    动画属性的类别。取值:"auto" | "XML" | "CSS"。
    auto:默认值,自动识别(他会优先当成 CSS 处理,如果无法识别,则直接当成 XML 类别处理。如果不能确定,建议不设置 attributeType 值)
    选择 XML 和 CSS 这两个值,取决于属性在 XML 里还是在 style 里(x, y 以及 transform 就属于 XML,opacity 就属于 CSS)

  4. from, to, by, values 属性:
    from:动画的起始值(可选,当不写的时候会取默认值)
    to:指定动画的结束值
    by:动画的相对变化值
    values:可以是分号分隔的一个或多个值(即:一组数值。但只有一个值时,无法体现动画效果)

  5. begin, end 属性:
    指动画开始的时间。如果把 beigin 值设置为 1s 就表示延迟1秒后开始动画,只是最简单的使用方法。
    还可以定义用分号分隔的一组值,例 beigin="1s;3s" 表示 1s 时执行一次,3s 再执行一次(如果之前动画没结束,会立即停止后续效果,从头开始)。
    所以,如果动画的持续时间为 dur="2s",同时没有 repeatCount 属性时候,我们可以看到动画似乎连续执行了2次。(时间单位: "h" | "min" | "s" | "ms")

  6. dur 属性:
    动画的持续时间。

Demo3:让动画停留在最后一帧

根据上面的Demo,我们已经实现了一个简单的 SVG 动画。
此时的 attributeName="r",即代表控制 circle 标签的 r 属性from="50" 代表动画开始圆形的 50to="80" 动画结束圆形半径过度到 80。
但可以发现,动画在结束后又返回到了起始帧(即:动画开始时的状态),那需要让他停留在结束帧(即:动画结束时的状态),就要给
` 标签添加 fill 属性。

Demo3

<svg width="400" height="400">
  <circle cx="200" cy="200" r="50" fill="#29ABe2" >
    <animate attributeName="r" attributeType="XML" from="50" to="80" begin="0s" dur="2s" fill="freeze"/>
  </circle>
</svg>
  1. fill 属性:
    表示动画间隙的填充方式。取值:"remove" | "freeze"。
    remove:默认值。表示动画结束后直接回到动画开始状态。
    freeze:冻结的意思。表示动画结束后保持动画结束时的状态。

  2. 主要注意一点:
    SVG fill 属性和 animate fill 属性是两个独立的属性。

Demo4:动画无限循环

效果只执行一次是不够的,很多时候一些元素动画需要重复循环。我们继续在 animate 标签中添加一个 repeatCount 属性。

Demo4

<svg width="400" height="400">
  <circle cx="200" cy="200" r="50" fill="#29ABe2" >
    <animate attributeName="r" attributeType="XML" from="50" to="80" begin="0s" dur="2s" fill="freeze" repeatCount="indefinite"/>
  </circle>
</svg>
  1. repeatCount 属性:
    表示动画执行次数。可以是一个数字,也可以是 "indefinite" 无限循环。
    另外还有一个 repeatDur 属性:表示动画重复的总时间。即:repeatDur = "1s" 那么动画执行到一半时就会暂停。

Demo5:实现呼吸效果

无限循环现得以实现,可流畅度还远远不够。起始帧和结束帧之间存在一个生硬的跳转,没有很好的衔接。我们改变一下 values 属性的数值,制作出我们想要的呼吸效果。

Demo5

<svg width="400" height="400">
  <circle cx="200" cy="200" r="50" fill="#29ABe2" >
    <animate attributeName="r" attributeType="XML" values="50;80;50" begin="0s" dur="2s" fill="freeze" repeatCount="indefinite"/>
  </circle>
</svg>
  1. from, to, by, values 相互之间存在制约关系,有以下一些规则:
    如果动画的起始值与元素的默认值是一样的,from 参数可以省略。
    不考虑values的前提下,to, by 两个参数至少需要有一个出现,否则动画效果没有。to 表示绝对值,by 表示相对值。拿位移距离,如果 from 是 0,to 值为 100 则表示元素从 0 移动到 100 这个位置。但是,如果 by 值是200,则表示移动到 100+200=300 这个位置。
    如果 to, by 同时出现,只识别to。
    如果 to, by, values 都没设置,自然没动画效果。如果任意(包括from)一个属性的值不合法,规范上说是没有动画效果。但是,据我测试,FireFox 浏览器确实如此,但是 Chrome 特意做了写容错处理。例如,本来是数值的属性,写了个诸如a这个不合法的值,其会当作 0 来处理,动画效果依然存在。
    当 values 值设置并能识别时候 from,to, by 的值都会被忽略。那 values 属性是干什么的呢?别看名字挺大众的,其还是有些功力的。我们实现动画,不可能就是单纯的从a位置到b位置,有时候,需要去c位置过渡下。此时,实际上有3个动画关键点,而 from, to, by 只能驾驭两个。

Demo6:多个动画效果同时执行

看到这,想必你已对 SVG 有了一个初步认知。现在我们改进一下代码,再增加一个 <animate> 标签,从而让元素能够同时实现2个效果。

Demo6

<svg width="400" height="400">
  <circle cx="200" cy="200" r="50" fill="#29ABe2" >
    <animate attributeName="r" attributeType="CSS" values="50;80;50" begin="0s" dur="2s" fill="freeze" repeatCount="indefinite"/>
    <animate attributeName="fill-opacity" attributeType="CSS" values="1;0.5;1" begin="0s" dur="2s" fill="freeze" repeatCount="indefinite"/>
  </circle>
</svg>
  1. attributeName="fill-opacity" 定义填充颜色透明度。

  2. 更多的 SVG 属性,可以查看 SVG 属性参考列表:// https://developer.mozilla.org/zh-CN/docs/Web/SVG/Attribute

Demo7:SVG 动画队列

多个效果的同时实现是不是没想象中的那么复杂?那我们再来实现一个 SVG 的动画队列,原理也十分简单。
为每个 <animate> 标签添 id,然后下一个动画的 begin 值为上一个的 id.end

Demo7

<svg width="400" height="400">
  <circle cx="50" cy="25" r="25" fill="#29ABe2" >
    <animate id="r1" attributeName="cy" attributeType="XML" from="25" to="375" begin="0s" dur=".2s" fill="freeze"/>
  </circle>
  <circle cx="110" cy="25" r="25" fill="#29ABe2" >
    <animate id="r2" attributeName="cy" attributeType="XML" from="25" to="375" begin="r1.end + .5s" dur=".2s" fill="freeze"/>
  </circle>
  <circle cx="170" cy="25" r="25" fill="#29ABe2" >
    <animate id="r3" attributeName="cy" attributeType="XML" from="25" to="375" begin="r2.end + .5s" dur=".2s" fill="freeze"/>
  </circle>
  <circle cx="230" cy="25" r="25" fill="#29ABe2" >
    <animate id="r4" attributeName="cy" attributeType="XML" from="25" to="375" begin="r3.end + .5s" dur=".2s" fill="freeze"/>
  </circle>
  <circle cx="290" cy="25" r="25" fill="#29ABe2" >
    <animate id="r5" attributeName="cy" attributeType="XML" from="25" to="375" begin="r4.end + .5s" dur=".2s" fill="freeze"/>
  </circle>
  <circle cx="350" cy="25" r="25" fill="#29ABe2" >
    <animate id="r6" attributeName="cy" attributeType="XML" from="25" to="375" begin="r5.end + .5s" dur=".2s" fill="freeze"/>
  </circle>
</svg>
  1. begin 的值可以是表达式:begin = [元素的id].[事件类型] +/- 时间值。例如:begin="r1.end + .5s" (即上一个动画完成后,延迟 0.5s 再开始)

  2. 同时为了更好的理解 attributeName 属性,我们把参数刚改为cy。

Demo8:动画旋转

接着,为了引出后面的内容,首先 SVG 元素旋转起来。为了更好的看出效果,把我们之前所画的圆形图案,改成为矩形。
并使用 line 标签,为 SVG 画布添加两条“辅助线”,便于更好的理解其中参数。

Demo8

<svg width="400" height="400">
  <rect width="100" height="100" x="100" y="100" fill="#29ABe2" >
    <animateTransform attributeName="transform" attributeType="XML" type="rotate" from="0 200 200" to="360 200 200" begin="0s" dur="5s" repeatCount="indefinite"/>
  </rect>
  <line x1="400" y1="200" x2="0" y2="200" stroke="black" />
  <line x1="200" y1="400" x2="200" y2="0" stroke="black" />
</svg>
<svg width="400" height="400">
  <rect width="100" height="100" x="150" y="150" fill="#29ABe2" >
    <animateTransform attributeName="transform" attributeType="XML" type="rotate" from="0 200 200" to="360 200 200" begin="0s" dur="5s" repeatCount="indefinite"/>
  </rect>
  <line x1="400" y1="200" x2="0" y2="200" stroke="black" />
  <line x1="200" y1="400" x2="200" y2="0" stroke="black" />
</svg>
  1. 除了SMIL规范中4个基本的动画元素,还引入了其他一些扩展动画:
    <animateTransform>:修改 SVG 的 transformation 属性(例如:transform属性,transform 的 type 可以包括:translate、scale、skew等,与CSS3相通)
    <mpath>:允许 <animateMotion> 引用一个外部定义的 <path>,让元素按这个 <path> 来运动
    path 属性:作为 <animateMotion> 的属性,来指定动画路径
    keyPoints 属性:作为 <animateMotion> 的属性,来控制路径动画的速度
    rotate 属性:作为 <animateMotion> 的属性,来控图形根据运动路径的角度(切线方向)来改变自身角度

  2. from 和 to都是三个值:角度(即:从0到360度的旋转)、坐标(即:这个坐标决定了svg围绕哪个点来进行旋转)

Demo9:路径动画,让图形沿着路径运动

CSS中无法实现的路径动画,在 SVG 中可以轻松实现。首先使用 path 元素画一条路径,然后通过 stroke 属性设置辅助线的宽度。

Demo9

<svg width="400" height="400">
  <path d="M100 100, A100 100, -45 0 1, 300 300 A100 100, -45 0 1, 100 100" stroke="#ccc" stroke-width="2" fill="none"/>
  <rect width="100" height="100" fill="#29ABe2" >
    <animateMotion dur="6s" repeatCount="indefinite" path="M0 0, A100 100, -45 0 1, 300 300 A100 100, -45 0 1, 0 0"/>
  </rect>
  <line x1="400" y1="200" x2="0" y2="200" stroke="black" />
  <line x1="200" y1="400" x2="200" y2="0" stroke="black" />
</svg>
  1. 关于 SVG ARC 路径,可以通过该模拟器了解:http://users.ecs.soton.ac.uk/rfp07r/interactive-svg-examples/

Demo10:路径动画,让图形随着你的路径自动做角度的调整

样子是实现了,但角度方面有些僵硬,不能随着 SVG 路径做调整。加入 rotate="auto" 这个属性便会让你的图形随着你的路径自动做角度的调整

Demo10

<svg width="400" height="400">
  <path d="M100 100, A100 100, -45 0 1, 300 300 A100 100, -45 0 1, 100 100" stroke="#ccc" stroke-width="2" fill="none"/>
  <rect width="100" height="100" fill="#29ABe2" >
    <animateMotion dur="6s" repeatCount="indefinite" path="M100 100, A100 100, -45 0 1, 300 300 A100 100, -45 0 1, 100 100" rotate="auto"/>
  </rect>
  <line x1="400" y1="200" x2="0" y2="200" stroke="black" />
  <line x1="200" y1="400" x2="200" y2="0" stroke="black" />
</svg>
  1. rotate 属性:
    取值:"auto" | "auto-reverse" | "固定数值"
    auto:根据运动路径的角度(切线方向)来改变自身角度
    auto-reverse:根据 auto 做一个反转镜像
    固定数值: 格式 rotate=(angle) 和 rotate=(angle, cx, cy),其中 angle 表示旋转角度;cx, cy表示旋转的中心点

Demo11:手动播放动画

我们通过 SMIL 内置的 API,就可以实现鼠标事件响应。首先我们给 SVG 添加一个 id 命名为 startRotation,然后把 begin 属性改为 begin="startRotation.click"

Demo11

<svg width="400" height="400" id="startRotation">
  <path d="M100 100, A100 100, -45 0 1, 300 300 A100 100, -45 0 1, 100 100" stroke="#ccc" stroke-width="2" fill="none"/>
  <rect width="100" height="100" fill="#29ABe2" >
    <animateMotion dur="6s" repeatCount="indefinite" path="M80 80, A100 100, -45 0 1, 320 320 A100 100, -45 0 1, 80 80" rotate="auto" begin="startRotation.click"/>
  </rect>
  <line x1="400" y1="200" x2="0" y2="200" stroke="black" />
  <line x1="200" y1="400" x2="200" y2="0" stroke="black" />
</svg>
  1. SVG鼠标事件响应有四种写法:SMIL方式、Attributes方式、JS+SMIL方式、EventListener方式

Demo12:制作Loading动画

根据上面所学习到的内容,做个简单的 SVG Loading 动画

Demo12

<svg width="400" height="400">
  <circle fill="none" stroke="#29ABe2" stroke-width="4" stroke-miterlimit="10" cx="200" cy="200" r="100"></circle>
  <line fill="none" stroke-linecap="round" stroke="#29ABe2" stroke-width="4" stroke-miterlimit="10" x1="200" y1="200" x2="280" y2="200">
    <animateTransform attributeName="transform" dur="2s" type="rotate" from="0 200 200" to="360 200 200" repeatCount="indefinite"></animateTransform>
  </line>
  <line fill="none" stroke-linecap="round" stroke="#29ABe2" stroke-width="4" stroke-miterlimit="10" x1="200" y1="200" x2="199.5" y2="260">
    <animateTransform attributeName="transform" dur="15s" type="rotate" from="0 200 200" to="360 200 200" repeatCount="indefinite"></animateTransform>
  </line>
</svg>
  1. stroke 是 SVG 中有个比较重要的属性分支,用来定义线条、文本或元素轮廓颜色(也可以通俗的理解为“描边”)
    stroke: 表示描边颜色。
    stroke-width: 表示描边的粗细。
    stroke-linecap: 表示描边端点表现方式。可用值有:butt, round, square, inherit
    stroke-linejoin: 表示描边转角的表现方式。可用值有:miter, round, bevel, inherit
    stroke-miterlimit: 表示描边相交(锐角)的表现方式。默认大小是4。定义了两条相交成一定角的线在连接处的圆滑程度。
    stroke-dasharray: 表示虚线描边。可选值为:none, <dasharray>, inherit.
    none:表示不是虚线;
    :为一个逗号或空格分隔的数值列表。表示各个虚线端的长度。可以是固定的长度值,也可以是百分比值;
    inherit:表继承。
    stroke-dashoffset: 表示虚线的起始偏移。可选值为:<percentage>, <length>, inherit. 百分比值,长度值,继承。
    stroke-opacity: 表示描边透明度。默认是1(合法的范围是:0 - 1)

Demo13:动画的暂停与播放

之前我们只是单纯的实现了动画的播放,那怎么实现动画的暂停和播放?此时就需要配合JS来达到我们所想要的效果

Demo13

<svg width="400" height="400" onclick="if (this.paused != true) { this.pauseAnimations(); this.paused = true; } else  { this.unpauseAnimations(); this.paused = false; }">
  <circle fill="none" stroke="#29ABe2" stroke-width="4" stroke-miterlimit="10" cx="200" cy="200" r="100"></circle>
  <line fill="none" stroke-linecap="round" stroke="#29ABe2" stroke-width="4" stroke-miterlimit="10" x1="200" y1="200" x2="280" y2="200">
    <animateTransform attributeName="transform" dur="2s" type="rotate" from="0 200 200" to="360 200 200" repeatCount="indefinite"></animateTransform>
  </line>
  <line fill="none" stroke-linecap="round" stroke="#29ABe2" stroke-width="4" stroke-miterlimit="10" x1="200" y1="200" x2="199.5" y2="260">
    <animateTransform attributeName="transform" dur="15s" type="rotate" from="0 200 200" to="360 200 200" repeatCount="indefinite"></animateTransform>
  </line>
</svg>
  1. 暂停:svg.pauseAnimations(); 播放:svg.unpauseAnimations(); 这里的 svg 指的是当前的 SVG DOM 元素。

上面的动画效果都是使用 SVG Animations(SMIL) 来实现的。
不过细心的你可能已经发现在 Chrome 下运行,控制台会提示错误信息(SVG's SMIL animations (<\animate>, <\set>, etc.) are deprecated and will be removed. Please use CSS animations or Web animations instead.)。这是由于自 Chrome 45 和 Opera 32 版本开始,就准备将 SVG SMIL 动画弃用,从而使用 CSS animationsWeb Animations API 来替代。不过就目前来看 Web Animations API 的兼容性还不太理想,但也说明了这是未来的一个必然趋势。对这方便内容有的兴趣的话,可以自己多了解一些。

参考资料:
https://www.chengrang.com/svg-animate.html
http://www.zhangxinxu.com/wordpress/2014/08/so-powerful-svg-smil-animation/
https://www.ibm.com/developerworks/cn/web/wa-scalable/
http://editor.method.ac/