最近看到篇文章很有同感,很多时候想学习一些东西,会被一堆堆的代码所困扰,很难去读懂和理解。而用一个个小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>
- 属性说明:
fill 属性:表示填充颜色
fill-opacity 属性:填充颜色透明度(合法的范围是:0 - 1)
stroke 属性:表示描边颜色(是 SVG 中有个比较重要的属性分支,下文中会有详细说明)
stroke-width 属性:表示描边的粗细
stroke-opacity 属性:表示描边透明度(合法的范围是:0 - 1) -
<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 - 同步多媒体集成语言)
来实现。
<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>
- 根据SMIL动画规范,有以下4个基本的动画元素:
<animate>
:定义动画的实时属性(例如:持续时间、属性和属性值)
<set>
:在指定的时间之后执行另外的动画效果,通常用来修改非数字类型属性所产生的动画
<animateMotion>
:让元素沿着特定的路径运动
<animateColor>
:在指定的时间内修改元素的颜色(SVG 2.0 中已被移除,使用<animate>
也能实现相同效果) -
attributeName 属性:
动画属性的名称。可以是元素直接暴露的属性,可以是CSS属性。 -
attributeType 属性:
动画属性的类别。取值:"auto" | "XML" | "CSS"。
auto:默认值,自动识别(他会优先当成 CSS 处理,如果无法识别,则直接当成 XML 类别处理。如果不能确定,建议不设置 attributeType 值)
选择 XML 和 CSS 这两个值,取决于属性在 XML 里还是在 style 里(x, y 以及 transform 就属于 XML,opacity 就属于 CSS) -
from, to, by, values 属性:
from:动画的起始值(可选,当不写的时候会取默认值)
to:指定动画的结束值
by:动画的相对变化值
values:可以是分号分隔的一个或多个值(即:一组数值。但只有一个值时,无法体现动画效果) -
begin, end 属性:
指动画开始的时间。如果把 beigin 值设置为 1s 就表示延迟1秒后开始动画,只是最简单的使用方法。
还可以定义用分号分隔的一组值,例 beigin="1s;3s" 表示 1s 时执行一次,3s 再执行一次(如果之前动画没结束,会立即停止后续效果,从头开始)。
所以,如果动画的持续时间为 dur="2s",同时没有 repeatCount 属性时候,我们可以看到动画似乎连续执行了2次。(时间单位: "h" | "min" | "s" | "ms") -
dur 属性:
动画的持续时间。
Demo3:让动画停留在最后一帧
根据上面的Demo,我们已经实现了一个简单的 SVG 动画。
此时的 attributeName="r",即代表控制 circle 标签的 r 属性
。from="50" 代表动画开始圆形的 50
,to="80" 动画结束圆形半径过度到 80。
但可以发现,动画在结束后又返回到了起始帧(即:动画开始时的状态),那需要让他停留在结束帧(即:动画结束时的状态),就要给
<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>
- fill 属性:
表示动画间隙的填充方式。取值:"remove" | "freeze"。
remove:默认值。表示动画结束后直接回到动画开始状态。
freeze:冻结的意思。表示动画结束后保持动画结束时的状态。 -
主要注意一点:
SVG fill 属性和 animate fill 属性是两个独立的属性。
Demo4:动画无限循环
效果只执行一次是不够的,很多时候一些元素动画需要重复循环。我们继续在 animate 标签中添加一个 repeatCount 属性。
<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>
- repeatCount 属性:
表示动画执行次数。可以是一个数字,也可以是 "indefinite" 无限循环。
另外还有一个 repeatDur 属性:表示动画重复的总时间。即:repeatDur = "1s" 那么动画执行到一半时就会暂停。
Demo5:实现呼吸效果
无限循环现得以实现,可流畅度还远远不够。起始帧和结束帧之间存在一个生硬的跳转,没有很好的衔接。我们改变一下 values 属性的数值,制作出我们想要的呼吸效果。
<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>
- 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个效果。
<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>
- attributeName="fill-opacity" 定义填充颜色透明度。
-
更多的 SVG 属性,可以查看 SVG 属性参考列表:// https://developer.mozilla.org/zh-CN/docs/Web/SVG/Attribute
Demo7:SVG 动画队列
多个效果的同时实现是不是没想象中的那么复杂?那我们再来实现一个 SVG 的动画队列,原理也十分简单。
为每个 <animate>
标签添 id
,然后下一个动画的 begin 值为上一个的 id.end
。
<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>
- begin 的值可以是表达式:begin = [元素的id].[事件类型] +/- 时间值。例如:begin="r1.end + .5s" (即上一个动画完成后,延迟 0.5s 再开始)
-
同时为了更好的理解 attributeName 属性,我们把参数刚改为cy。
Demo8:动画旋转
接着,为了引出后面的内容,首先 SVG 元素旋转起来。为了更好的看出效果,把我们之前所画的圆形图案,改成为矩形。
并使用 line 标签,为 SVG 画布添加两条“辅助线”,便于更好的理解其中参数。
<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>
- 除了SMIL规范中4个基本的动画元素,还引入了其他一些扩展动画:
<animateTransform>
:修改 SVG 的 transformation 属性(例如:transform属性,transform 的 type 可以包括:translate、scale、skew等,与CSS3相通)
<mpath>
:允许<animateMotion>
引用一个外部定义的<path>
,让元素按这个<path>
来运动
path 属性:作为<animateMotion>
的属性,来指定动画路径
keyPoints 属性:作为<animateMotion>
的属性,来控制路径动画的速度
rotate 属性:作为<animateMotion>
的属性,来控图形根据运动路径的角度(切线方向)来改变自身角度 -
from 和 to都是三个值:角度(即:从0到360度的旋转)、坐标(即:这个坐标决定了svg围绕哪个点来进行旋转)
Demo9:路径动画,让图形沿着路径运动
CSS中无法实现的路径动画,在 SVG 中可以轻松实现。首先使用 path 元素画一条路径,然后通过 stroke 属性设置辅助线的宽度。
<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>
- 关于 SVG ARC 路径,可以通过该模拟器了解:http://users.ecs.soton.ac.uk/rfp07r/interactive-svg-examples/
Demo10:路径动画,让图形随着你的路径自动做角度的调整
样子是实现了,但角度方面有些僵硬,不能随着 SVG 路径做调整。加入 rotate="auto"
这个属性便会让你的图形随着你的路径自动做角度的调整
<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>
- 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"
<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>
- SVG鼠标事件响应有四种写法:SMIL方式、Attributes方式、JS+SMIL方式、EventListener方式
Demo12:制作Loading动画
根据上面所学习到的内容,做个简单的 SVG Loading 动画
<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>
- 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来达到我们所想要的效果
<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>
- 暂停: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 animations
和Web 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/