最近看到一篇很有趣的关于动画的文章,想拿来做个练习,里面用到了SVG,所以花了一些时间了解了一下SVG,在这里做一个记录,方便自查
所有文章均同步到我的博客
SVG是神马?
- SVG指可伸缩矢量图
- 用来定义基于矢量的网络图形
- 使用XML格式来定义
- 在放大或缩小的情况下,图形质量不受影响
- 在2013年成为W3C推荐标准
- 浏览器支持情况:IE9+,chrome 33.0+,Firefox 28.0+,Safari 7.0+
SVG使用方式
浏览器直接打开
.svg
文件直接用浏览器打开
在HTML中使用
用<iframe>
嵌入SVG
<iframe src="xx.svg" width="200" height="200" ></iframe>
用<img>
嵌入SVG
<img src="xx.svg" width="300" />
用CSS背景图的方式嵌入SVG
div {
background: url('xx.svg') no-repeat center;
background-size : 200px 200px;
}
直接使用<svg>
元素
<svg width="600px" height="300px" viewBox="0 0 250 250">
<circle cx="35" cy="35" r="35" style="stroke: black; fill: none;"/>
<rect x="150" y="50" width="200" height="100" style="stroke: blue;
fill: none;"/>
<svg x="150px" y="50px" width="200px" height="100px" viewBox="0 0 125 125" preserveAspectRatio="xMaxYMax meet">
<circle cx="35" cy="35" r="35" style="stroke: black; fill: rgba(0,0,0,.5);"/>
</svg>
</svg>
后面还有两种embed和object方式,不建议使用,就不做介绍了。
常用<img>
和<svg>
,做背景图时用background-image
引入
SVG结构
.svg
文件
常见写法:
<?xml version="1.0"?> //XML声明
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="600" height="300">
<!-- SVG代码 -->
</svg>
//xmlns="http://www.w3.org/2000/svg"是声明空间
直接在HTML中使用<svg>
<svg width="600px" height="300px" viewBox="0 0 250 250">
<circle cx="35" cy="35" r="35" style="stroke: black; fill: none;"/>
<rect x="150" y="50" width="200" height="100" style="stroke: blue;
fill: none;"/>
<svg x="150px" y="50px" width="200px" height="100px" viewBox="0 0 125 125" preserveAspectRatio="xMaxYMax meet">
<circle cx="35" cy="35" r="35" style="stroke: black; fill: rgba(0,0,0,.5);"/>
</svg>
</svg>
注意不用再写XML声明了。
SVG图形、属性及基本操作
基本图形
- 矩形
<rect>
- 圆形
<circle>
- 椭圆
<ellipse>
- 线
<line>
- 折线
<polyline>
- 多边形
<polygon>
- 路径
<path>
后面会重点介绍
基本属性
- fill 填充颜色(rgb 值、颜色名或者十六进制值)
- stroke 定义图形边框的颜色
- stroke-width 定义图形边框的宽度
- transform 声明自身坐标相对父级坐标系的变换,很重要的属性,后面会深入探讨
基本操作
- 创建图形,
document.createElementNS(ns, tagName)
, ns是svg的命名空间 - 添加图形,
element.appendChild(childElement)
- 设置/获取属性,
element.setAttrbute(name, value)
,element.getAttribute(name)
比如:
var SVG_NS = 'http://www.w3.org/2000/svg';
var svg = document.createElementNS(SVG_NS, 'svg');
svg.setAttribute('width', '100%');
svg.setAttribute('height', '100%');
document.getElementById('svg-wrap').appendChild(svg);
坐标系统
重点来了,SVG的坐标系统
首先介绍一下SVG两个很重要的概念:viewport,viewBox
viewport
viewport,也可以叫做视窗,表示SVG可见区域的大小,超出这个区域的图形将不被显示出来,相当于将要作画的画板大小,在<svg>
中通过width
和height
来控制SVG的viewport的大小,注:若不加单位,则默认为px
<svg width="400" height="200" style="border:1px solid #7798CB">
<rect x="30" y="10" width="30" height="15" fill="#5CC26F" />
</svg>
效果如下,在svg可视区范围内,也就是viewport里的坐标为(30,10)的地方放了一个绿色矩形:
viewBox
viewBox, 也可以叫视野,这个矩形区域定义了我们要如何去观看svg在viewport上渲染出来的世界,借用张鑫旭大神的一个很好的比喻:“SVG就像是我们的显示器屏幕,viewBox就是截屏工具选中的那个框框,最终的呈现就是把框框中的截屏内容再次在显示器中全屏显示”。这个在地图里应用很广泛,类似百度地图,谷歌地图,滑动滚轮就可以让地图放大缩小的效果。
说了这么多,还是动手写几个例子来体会一下,其中viewBox="x(左上角横坐标), y(左上角纵坐标), width, height"
<svg width="400" height="200" viewBox="0,0,400,200" style="border:1px solid #7798CB">
<rect x="30" y="10" width="30" height="15" fill="#5CC26F" />
</svg>
效果如下:
因为viewBox的宽高和svg的一样,相当于我透过跟svg一样大的窗户去看svg的世界,所以效果没有任何变化
修改一下viewBox的宽高
<svg width="400" height="200" viewBox="0,0,40,20" style="border:1px solid #7798CB">
<rect x="30" y="10" width="30" height="15" fill="#5CC26F" />
</svg>
这时,我们看到svg里面的小矩形跑到了svg边框的右下角,同时还被放大到300*150,超出svg的部分不显示,有木有感受到viewBox的巨大威力~~
这里为啥小矩形会被“挪到”右下角呢,这是因为viewBox的大小是40*20,而且从坐标(0,0)处开始,而rect的坐标是(30,10),那么我用viewBox去圈viewport的时候,rect就会出现在viewBox的右下方。专门做了一张图,帮助理解
上图比较形象的解释了小矩形的变化,接着将viewBox在viewport里全屏展示,这时rect的宽高会10倍放大,因为viewBox和viewport的width比例和height比例都是1/10,同时viewBox的宽高比和viewport的宽高比一致,都是2/1,那么rect的宽高都会放大10倍。
这是一种比较特殊的情况,当然也有两个比例不一样的,这时就会使得图形变形扭曲(因为宽高不能等比例缩放),那么属性preserveAspectRatio
就需要用来强制统一缩放比来保持图形的宽高比。
preserveAspectRatio
preserveAspectRatio
是svg的一个属性,作用对象是viewBox,值为两个用空格分割的值组合而成。
作用:用来声明viewBox在视窗viewport中如何定位
preserveAspectRatio="xMidYMid meet"
第一个值,表示viewBox如何与viewport对齐:
- xMin viewport和viewBox左边对齐
- xMid viewport和viewBox x轴中心对齐
- xMax viewport和viewBox右边对齐
- YMin viewport和viewBox上边缘对齐
- YMid viewport和viewBox y轴中心点对齐
- YMax viewport和viewBox下边缘对齐
x,y可以自由组合
第二个值的含义:
这个参数用来声明是否强制统一缩放,也就是如何来维持宽高比,如果是,那么该方法定义的规则会在viewBox的宽高比不等于viewport宽高比的时候生效。这里我刚看的时候感觉理解上不是很容易,下面就列出我对这个知识点的理解方式,如果有错欢迎大家一起探讨。
- none 在viewBox的宽高比不等于viewport宽高比时,不会保持图形的宽高比,图形会扭曲变形
- meet 默认值,保持图形的宽高比进行缩放,使viewport包含viewBox
动手写一个例子,帮助理解
<svg width="400" height="200" viewBox="0,0,40,40" preserveAspectRatio="xMidYMid meet" style="border:1px solid #7798CB">
<rect x="0" y="0" width="30" height="15" fill="#5CC26F" />
</svg>
效果:
从图中看到rect被放大到150*75
,怎么计算的呢?
viewBox现在是40*4
0,宽高比为1,而viewport是400*200
宽高比为2,二者现在不相等,viewport与viewBox的宽度比为:wr=400/40=10/1
,高度比为:hr=200/40=5/1
,由于指定的是meet,那么就要达到viewport包含viewBox的效果,同时保持viewBox的宽高比,这时viewBox该怎么同时满足这两个条件呢?
viewBox还是很聪明的,它会按wr
和hr
中较小的那个值来进行宽高等比例缩放,这样就既能保持宽高比,还能被viewport包含啦~上例中得viewBox及里面的矩形rect的宽高就会都按5倍放大。
slice 保持viewbox的宽高比,使viewBox包含viewport
<svg width="400" height="200" viewBox="0,0,40,40" preserveAspectRatio="xMidYMid slice" style="border:1px solid #7798CB">
<rect x="0" y="0" width="30" height="15" fill="#5CC26F" />
</svg>
效果:
现在是slice,那么就需要达到viewBox包含viewport的效果,这时候聪明的viewBox就会选择wr
和hr
中值较大的那个进行放大(这里是因为viewBox比viewport小,所以需要放大来包含viewport,如果viewBox比viewport大,那么就需要缩小了,依旧乘以比例较大的那个值),也就是放大10倍,此时rect变为300*150,超出viewport的部分不显示,有剪切的效果。
图形分组
- svg里使用
<g></g>
来创建分组 - 在
<g>
中创建的属性,子元素是可以继承的 - 在
<g>
中可以用transform
来定义坐标变换,坐标变换会在后面做详细解释 -
<g>
可以嵌套使用
<svg>
<g stroke="blue" fill="none" transform="translate(0, 50)">
<rect x="0" y="0" width="30" height="15" />
<circle cx="65" cy="75" r="10" />
</g>
</svg>
这么一来就可以通过一个g,来控制包含在里面的所有图形的位置,颜色等属性。控制位移,那么就需要了解svg里的坐标系统了~且听下面的分析介绍~~很重要的概念
svg的坐标系统
svg中的坐标系是这样的:
水平向右是x轴的正方向,竖直向下是y轴的正方向,角度的正方向为顺时针方向
四个比较重要的坐标系:用户坐标系,自身坐标系,前驱坐标系,参考坐标系
用户坐标系
svg的坐标系,最原始的坐标系,其他几个坐标系都是从用户坐标系展开的。
viewBox就是定义了在用户坐标系的哪个位置上看svg,如下图所示
自身坐标系
自身坐标系是图形自身,或者分组的坐标系
比如
<svg>
<rect x="0" y="0" width="30" height="15" fill="#5CC26F" />
</svg>
这个矩形自身有一个坐标系,rect里的x,y,width,height等属性都是根据其自身坐标系来定义的。
图中绿色的坐标系就是rect的自身坐标系
前驱坐标系
前驱坐标系就是父容器的坐标系,上图中,rect的前驱坐标系就是用户坐标系。
坐标变换实际上就是前驱坐标系经过一系列变换之后得到自身坐标系的过程。
<svg>
<rect x="0" y="0" width="30" height="15" transform="translate(50,50)" fill="#5CC26F" />
</svg>
经过transform进行水平和竖直方向的50px的坐标偏移之后,rect的自身坐标系就跑到了上图中绿色坐标系的位置。若rect的属性x,y分别有值,比如x="10",y="10",则是相对于自身坐标系而言的。
参考坐标系
选取一个坐标系作为参考坐标系,以此来描述图形的位置
<svg>
<rect x="0" y="0" width="30" height="15" transform="translate(50,50)" fill="#5CC26F" />
</svg>
还是这个例子,如果我们把用户坐标系作为参考坐标系,那么在参考坐标系下,矩形rect的位置就是(50,50)。
坐标变换
svg中的坐标变换指的是将一个坐标系变换到另一个坐标系的过程描述。
坐标变换中起到很大作用的一个属性,就是上面用到过的transform
transform就是定义了前驱坐标系到自身坐标系的一个线性变换
语法:
-
translate(<x>,<y>)
x代表x轴上的移动的值,y代表y轴上的移动的值。 -
rotate(<deg>)
按角度deg进行旋转,自身坐标系相对于其前驱坐标系进行旋转 -
scale(<sx>,<sy>)
sx代表沿x轴的缩放值,用来水平延长或者拉伸元素;sy代表沿y轴缩放值,用来垂直延长或者缩放元素。 -
matrix(<a> <b> <c> <d> <e> <f>)
通过一个有6个值的变换矩阵声明一个变换。matrix(a,b,c,d,e,f)等同于添加变换matrix[a b c d e f]。较少使用。
注意:如果有连续变换,那么每次的变换都是基于前一个变换后得到的自身坐标系的基础上进行变换。
path
路径path表现的是一个图形的外边沿,可以被填充,可以作为笔画,可以作为裁剪路径,或者任意前面三种的组合。path实际上就是用指令来控制画笔,将画笔放到某一个点上,然后拖动画笔,画出直线或曲线。
path中的属性d用来指定路径位置和形状,指令如下:
- M 参数:x y
功能:将画笔移动到点(x,y) - L 参数:x y
功能:画笔从当前的点绘制线段到点(x,y) - H 参数:x
功能:画笔从当前的点绘制水平线段到点(x,y0) - V 参数:y
功能:画笔从当前的点绘制竖直线段到点(x0,y) - A 参数:rx ry x-axis-rotation large-arc-flag sweep-flag x y
功能:画笔从当前的点绘制一段圆弧到点(x,y) - C 参数:x1 y1, x2 y2, x y
功能:画笔从当前的点绘制一段三次贝塞尔曲线到点(x,y) - S 参数:x2 y2, x y
功能:光滑版本的三次贝塞尔曲线(省略第一个控制点)
注释:1.如果上一个曲线也是贝塞尔曲线,那么在与上一个曲线的交界处,省略的第一个控制点与上一个曲线的第二个控制点关于交界点是镜像关系;2.如果上一个曲线不是贝塞尔曲线,那么会把上一个图形的终点位置作为贝塞尔曲线的第一个控制点 - Q 参数:x1 y1, x y
功能:绘制二次贝塞尔曲线到点(x,y) - T 参数:x y
功能:光滑版本的二次贝塞尔曲线(省略控制点)
注释:同S的注释 - Z 无参数
功能:绘制闭合图形,如果d属性不指定Z命令,则绘制线段,而不是封闭图形。
注意:命令区分大小写,大写表示坐标参数为绝对位置,小写为相对位置(相对于当前画笔的相对位置);最后的参数表示最后要到达的位置;上个命令结束的位置就是下一个命令开始的位置;命令可以重复参数表示重复执行同一条命令。
属性可以连续使用,举个例子:
<svg>
<path d="M100 100 L150 300 L200 300 Z" />
</svg>
首先将画笔放到(100,100)处,然后移动画笔到(150,300)画出直线,然后又画直线到(200,300),最后在点(100,100)处关闭路径,形成闭合图形。
说一下弧线参数含义
A rx ry x-axis-rotation large-arc-flag sweep-flag x y
- rx,ry 指椭圆的两个半轴大小
- x-axis-rotation 指椭圆的x轴与水平方向顺时针方向夹角,可以想像成一个水平的椭圆绕中心点顺时针旋转的角度
- large-arc-flag 表示是否选择弧长较长的那段弧线,1表示大角度弧线,0为小角度弧线
- sweep-flag 确定起点至终点的方向,1为顺时针,0为逆时针
- x,y 弧的终点坐标
<svg width="100%" height="1000px">
<path d="M 100 100 h 100 l -100 100 v -100 M 200 100 A 100 100 0 0 1 100 200" fill="#fff" stroke="red"/>
</svg>
关于贝塞尔曲线,可以参考这里的demo加深理解,里面的起点和终点之间的点,就是贝塞尔曲线的控制点。