1.什么是SVG
SVG是XML语言的一种形式,有点类似XHTML,它可以用来绘制矢量图形。一个简单的SVG文档由<svg>
根元素和基本的形状元素构成。另外还有一个g元素,它用来把若干个基本形状编成一个组。 在学习SVG之前要注意一下两点:
- SVG的元素和属性必须按标准格式书写,因为XML是区分大小写的(这一点和html不同)
- SVG里的属性值必须用引号引起来,就算是数值也必须这样做。
2.基本属性填充和边框
1.填充
- file :设置对象内部的颜色;
- fill-opacity :控制填充色的不透明度;
2.边框
stroke :设置绘制对象的线条的颜色;
stroke-opacity :控制描边的不透明度;
stroke-width :定义了描边的宽度,描边是以路径为中心线绘制的;
stroke-linecap :控制边框终点的形状;
stroke-linecap属性的值有三种可能值:
butt 用直边结束线段,它是常规做法,线段边界90度垂直于描边的方向、贯穿它的终点;
square 的效果差不多,但是会稍微超出实际路径的范围,超出的大小由stroke-width控制;
round表示边框的终点是圆角,圆角的半径也是由stroke-width控制的。
<svg width="160" height="140" xmlns="http://www.w3.org/2000/svg" version="1.1"> <line x1="40" x2="120" y1="20" y2="20" stroke="black" stroke-width="20" stroke-linecap="butt"/> <line x1="40" x2="120" y1="60" y2="60" stroke="black" stroke-width="20" stroke-linecap="square"/> <line x1="40" x2="120" y1="100" y2="100" stroke="black" stroke-width="20" stroke-linecap="round"/> </svg>
注:实际上中间是没有粉色的线的,在此是为了更好的显示不同的效果。
stroke-linejoin:制两条描边线段之间,用什么方式连接,有三种可能值;
miter 默认值,表示用方形画笔在连接处形成尖角;
round 表示用圆角连接,实现平滑效果;
bevel 连接处会形成一个斜接;
<svg width="160" height="280" xmlns="http://www.w3.org/2000/svg" version="1.1"> <polyline points="40 60 80 20 120 60" stroke="black" stroke-width="20" stroke-linecap="butt" fill="none" stroke-linejoin="miter"/> <polyline points="40 140 80 100 120 140" stroke="black" stroke-width="20" stroke-linecap="round" fill="none" stroke-linejoin="round"/> <polyline points="40 220 80 180 120 220" stroke="black" stroke-width="20" stroke-linecap="square" fill="none" stroke-linejoin="bevel"/> </svg>
stroke-dasharra:将虚线类型应用在描边上;
stroke-dasharray属性的参数,是一组用逗号分割的数字组成的数列。和path不一样,这里的数字必须用逗号分割(空格会被忽略)。每一组数字,第一个用来表示填色区域的长度,第二个用来表示非填色区域的长度。
<svg width="200" height="150" xmlns="http://www.w3.org/2000/svg" version="1.1" style="border:1px solid #ddd"> <path d="M 10 75 Q 50 10 100 75 T 190 75" stroke="black" stroke-linecap="round" stroke-dasharray="5,10,2" fill="none" /> <path d="M 10 75 L 190 75" stroke="red" stroke-linecap="round" stroke-width="1" stroke-dasharray="5,5" fill="none" /> </svg>
fill-rule :定义如何给图形重叠的区域上色,有两种填充规则,更详细的说明可以看这里搞懂SVG/Canvas中nonzero和evenodd填充规则;
nonzero 非零填充
evenodd 奇偶填充
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg" version="1.1" style="border:1px solid #ddd"> <polygon fill-rule="nonzero" stroke="red" points="50,0 21,90 98,35 2,35 79,90" /> </svg> <svg width="100" height="100" xmlns="http://www.w3.org/2000/svg" version="1.1" style="border:1px solid #ddd"> <polygon fill-rule="evenodd" stroke="red" points="50,0 21,90 98,35 2,35 79,90" /> </svg>
stroke-miterlimit :定义什么情况下绘制或不绘制边框连接的miter效果;
stroke-dashoffset :定义虚线开始的位置。
3.SVG的基本形状
1. 矩形(rect)
<rect x="10" y="10" width="30" height="30"/>
<rect x="60" y="10" rx="10" ry="10" width="30" height="30"/>
- x:矩形左上角的x位置
- y:矩形左上角的y位置
- width:矩形宽度
- height:矩形高度
- rx:圆角的x方位的半径
- ry:圆角的y方位的半径
2. 圆形(circle)
<circle cx="25" cy="75" r="20"/>
- r:圆的半径
- cx cy :圆心的坐标(x,y)
3. 椭圆(ellipse)
<ellipse cx="75" cy="75" rx="20" ry="5"/>
- cx:椭圆的x半径
- cy:椭圆的y半径
- cx cy :圆心的坐标(x,y)
4. 线条(line)
<line x1="10" x2="50" y1="110" y2="150"/>
- x1 y1 :直线起始点坐标(x,y)
- x2 y2 :直线终点坐标(x,y)
5. 折线(polyline)
<polyline points="60 110, 65 120, 70 115, 75 130, 80 125, 85 140, 90 135, 95 150, 100 145"/>
- points :点集数列。每个数字用空白、逗号、终止命令符或者换行符分隔开。每个点必须包含2个数字,一个是x坐标,一个是y坐标。所以点列表 (0,0), (1,1) 和(2,2)可以写成这样:“0 0, 1 1, 2 2”。
6. 多边形(polygon)
<polygon points="50 160, 55 180, 70 180, 60 190, 65 205, 50 195, 35 205, 40 190, 30 180, 45 180"/>
- points :点集数列。每个数字用空白符、逗号、终止命令或者换行符分隔开。每个点必须包含2个数字,一个是x坐标,一个是y坐标。所以点列表 (0,0), (1,1) 和(2,2)可以写成这样:“0 0, 1 1, 2 2”。路径绘制完后闭合图形,所以最终的直线将从位置(2,2)连接到位置(0,0)。
7. 路径(path)
(1)直线命令
M命令 (move to)
需要两个参数分别是需要移动到点的x轴和y轴,使用M命令移动画笔后,只会移动画笔不画线,所以画线还需要另外三个命令L、H、V
M x y (or d dx dy)
L命令 (line to)
需要两个参数,分别是一个点的x轴和y轴坐标,L命令将会在当前位置和新位置(L前面画笔所在的点)之间画一条线段
L x y (or l dx dy)
<path d="M10 10 L 90 20 " fill="#f00" stroke="black"/>
H命令(水平绘线)
标明x轴移动到的位置,只在一个轴的方向上移动
H x (or h dx)
<path d="M10 10 H60 L 90 90 " fill="#f00" stroke="black"/>
V命令(垂直绘线)
标明y轴移动到的位置,只在一个轴的方向上移动
V y (or v dy)
<path d="M10 10 V90 L 11 90 " fill="#f00" stroke="black"/>
Z命令 (闭合路径)
从当前点画一条直线到路径的起点,通常被放在路径的最后
Z (or z)
<path d="M 10 10 h 70 v 80 h -35 Z" fill="#f00" stroke="black"/>
上述路径是:画笔移动到(10,10)点,由此开始,向右移动70像素构成一条水平线,然后向下移动80像素,然后向左移动35像素,然后再回到起点。
(2)曲线命令
三次贝塞尔曲线C命令
三次贝塞尔曲线需要定义一个点和两个控制点,需要设置三组坐标参数
C x1 y1, x2 y2, x y (or c dx1 dy1, dx2 dy2, dx dy)
这里的最后一个坐标(x,y)表示的是曲线的终点,另外两个坐标是控制点,(x1,y1)是起点的控制点,(x2,y2)是终点的控制点。控制点描述的是曲线起始点的斜率,曲线上各个点的斜率,是从起点斜率到终点斜率的渐变过程,斜率公式为
tanα = (y2-y1)/(x2-x1)
当tanα>0时,直线与x轴夹角越大,斜率越大;当k<0时,直线与x轴夹角越大,斜率越小。<path d="M 10 10 C 10 50,90 50 , 90 10" fill="#f00" stroke="black"/>
如上图所示,第一个控制点没有斜率,第二个控制点斜率 k = (10-20)/(90-80) = -1,角度为135°
S命令
S命令可以用来创建与之前那些曲线一样的贝塞尔曲线,但是,如果S命令跟在一个C命令或者另一个S命令的后面,它的第一个控制点,就会被假设成前一个控制点的对称点。如果S命令单独使用,前面没有C命令或者另一个S命令,那么它的两个控制点就会被假设为同一个点
S x2 y2, x y (or s dx2 dy2, dx dy)
<path d="M10 50 C 30 30 , 30 30, 50 50 S 80 80, 90 50 " fill="#f00" stroke="black"/>
注: S的第一个控制点是C命令的第二个控制点的对称点;
注:两个控制点相同
二次贝塞尔曲线Q
只需要一个控制点,用来确定起点和终点的曲线斜率,需要两组参数,控制点和终点坐标
Q x1 y1, x y (or q dx1 dy1, dx dy)
<path d="M10 50 Q 30 10 , 90 50 " fill="#f00" stroke="black"/>
T命令
T命令,可以通过更简短的参数,延长二次贝塞尔曲线。和之前一样,快捷命令T会通过前一个控制点,推断出一个新的控制点。这意味着,在你的第一个控制点后面,可以只定义终点,就创建出一个相当复杂的曲线。需要注意的是,T命令前面必须是一个Q命令,或者是另一个T命令,才能达到这种效果。如果T单独使用,那么控制点就会被认为和终点是同一个点,所以画出来的将是一条直线。
T x y (or t dx dy)
<path d="M10 50 Q 30 10 , 50 50 T 90 50" fill="#f00" stroke="black"/>
注:虽然三次贝塞尔曲线拥有更大的自由度,但是两种曲线能达到的效果总是差不多的。具体使用哪种曲线,通常取决于需求,以及对曲线对称性的依赖程度
(3)弧形
弧形命令A
基本上,弧形可以视为圆形或椭圆形的一部分。假设,已知椭圆形的长轴半径和短轴半径,并且已知两个点(在椭圆上),根据半径和两点,可以画出两个椭圆,在每个椭圆上根据两点都可以画出两种弧形。所以,仅仅根据半径和两点,可以画出四种弧形。为了保证创建的弧形唯一,A命令需要用到比较多的参数。
A rx ry x-axis-rotation large-arc-flag sweep-flag x y
a rx ry x-axis-rotation large-arc-flag sweep-flag dx dy
- rx :半径x
- ry :半径y
- x-axis-rotation :椭圆倾斜的角度,值为倾斜的角度值
- large-arc-flag :决定弧线是大于还是小于180度,0表示小角度弧,1表示大角度弧
- sweep-flag :弧线的方向,0表示从起点到终点沿逆时针画弧,1表示从起点到终点沿顺时针画弧
<path d="M10 50 A 20 30 45 0 1 60 70" fill="#f00" stroke="black"/>
注:起始点为(20 30),rx = 20, ry = 30 , x-axis-rotation = 45 , large-arc-flag = 0 (这里0或1没什么区别), sweep-flag = 1,终点为(60 70);
<path d="M10 50 A 20 30 0 0 0 60 70" fill="#f00" stroke="black"/>
如果你是从Canvas过渡到SVG,那么弧形会比较难以掌握,但它也是非常强大的。用路径来绘制完整的圆或者椭圆是比较困难的,因为圆上的任意点都可以是起点同时也是终点,无数种方案可以选择,真正的路径无法定义。通过绘制连续的路径段落,也可以达到近似的效果,但使用真正的circle或者ellipse元素会更容易一些。