通过上一节的学习,我们知道了怎么在SVG中绘制一些基本的图形。在SVG中除了可以通过<rect>
、<circle>
、<ellipse>
、<polygon>
、<line>
、<polygon>
来绘制矩形、圆形、椭圆、多边形、直线和折线等形状。除此之外,在SVG中还有一个<path>
元素,可以帮助我们在SVG中绘制任何你想要的形状。也有人说,掌握了SVG中的<path>
是学习SVG的重中之重。今天我们来了解SVG中有关于<path>
的相关知识。
这篇文章将会介绍有关于<path>
的相关知识,但我们不会只用一篇文章的篇幅来介绍,后续将会收集和整理更多有关于<path>
相关的内容。因为在SVG中所有的物件和元素都是由path
组成,所以path
就是具有相当多的指令让使用者来设定,也就是说,搞懂path
就相当于搞定了SVG。
Path相关指令
先来看看<path>
相关的指令(<path>
元素属性),这些指令可以帮助你了解和使用SVG的path
。其中有一点我们需要特别注意:参数大写代表绝对坐标,小写代表与前一个坐标的相对座标。先罗列一下有关于<path>
元素相关的指令:
指令 | 参数 | 描述 |
---|---|---|
M | x y | 起始点坐标x y (Move to) |
L | x y | 从当前点的坐标画直线到指定点的 x y 坐标 (Line to) |
H | x | 从当前点的坐标画水平直线到指定的x 轴坐标 (Horizontal line to) |
V | y | 从当前点的座标画垂直直线到指定的y 轴坐标 (Vertical line to) |
C | x1 y1 x2 y2 x y | 从当前点的坐标画条贝塞风线到指定点的x, y 坐标,其中 x1 y1 及x2, y2 为控制点 (Curve) |
S | x2 y2 x y | 从当前点的坐标画条反射的贝塞曲线到指定点的x, y 坐标,其中x2, y2 为反射的控制点(Smooth curve) |
Q | x1 y1 x y | 从当前点的坐标画条反射二次贝塞曲线到指定点的x, y 坐标,其中x1 y1 为控制点(Quadratic Bézier curve) |
T | x y | 从当前点的坐标画条反射二次贝塞曲线到指定点的x, y 坐标,以前一个坐标为反射控制点(Smooth Quadratic Bézier curve) |
A | rx ry x-axis-rotation large-arc-flag sweep-flag x y | 从当前点的坐标画个椭圆形到指定点的x, y 坐标,其中rx, ry 为椭圆形的x 轴及y 轴的半径,x-axis-rotation 是弧线与x 轴的旋转角度,large-arc-flag 则设定1 最大角度的弧线或是0 最小角度的弧线,sweep-flag 设定方向为1 顺时针方向或0 逆时针方向(Arc) |
Z | 关闭路径,将当前点坐标与第一个点的坐标连接起来(Closepath) |
制作图软件中的path
同样的,咱们先来看制作矢量图软件制作path
导出来的代码。还是拿Sketch制图软件来举例:
将上图导出为一个.svg
文件,然后在编辑器中打开文件:
<svg width="357px" height="552px" viewBox="0 0 357 552"><polyline id="Path" stroke="#979797" fill="none" stroke-width="3" points="2.12890625 2.1640625 265.90625 139.902344 323.003906 37.9804688"></polyline><polygon id="Path" fill="none" stroke="#BD10E0" stroke-width="3" points="2.12890625 412.164062 265.90625 549.902344 323.003906 447.980469 250.5625 487.46875"></polygon><path fill="none" d="M2.12890625,324.164062 C246.200156,411.01209 334.125937,456.924851 265.90625,461.902344 C198.734375,466.803385 217.766927,432.829427 323.003906,359.980469" id="Path" stroke="#50E3C2" stroke-width="3"></path><path fill="none" d="M2.12890625,232.164062 C75.0507812,323.229167 162.976562,369.141927 265.90625,369.902344 C360.395833,338.97526 379.428385,305.001302 323.003906,267.980469" id="Path" stroke="#417505" stroke-width="3"></path><path fill="none" d="M2.12890625,142.164062 C106.782552,268.945312 193.595052,309.457031 262.566406,263.699219 C331.53776,217.941406 351.683594,189.36849 323.003906,177.980469" id="Path" stroke="#BD10E0" stroke-width="3"></path><path fill="none" d="M2.12890625,82.1640625 L240.945204,206.868276 C254.848001,214.127988 272.003365,209.018721 279.66886,195.335469 L323.003906,117.980469" id="Path" stroke="#D31E1E" stroke-width="3"></path></svg>
把上面的代码内嵌到HTML中,效果如下:
导出来的代码,可以明显的看出来,第一个用的是<polyline>
绘制的折线,<polygon>
绘制的是多边线。其他的几个都是<path>
绘制的形状。而使用path
绘制的形状都是通过属性d
定义的,属性d
的值是一个“命令+参数”的序列(正如上表所示的命令参数),后续将会用一些简单的示例来阐述这些可用的命令。
每一个命令都是用一个关键字母来表示,比如,字母M
表示的是Move to
命令,当解析器读到这个命令时,它就知道你是打算移动到某个点。跟在命令字母后面的,是你需要移动到的那个点的x
和y
轴坐标。比如移动到(10, 10)
这个点的命令,就可以写成M 10 10
。这一段字符结束后,解析器就会去读下一段命令。每一个命令都有两种表达方式,一种是大写字母,表示采用绝对定们,另一种是小写字母,表示采用的是相对定位。上面示例代码采用的都是大写字母,表示采用的都是绝对坐标。
因为属性d
采用的是用户坐标系统,所以不需要标明单位。
看到一大串这样的代码:
<path fill="none" d="M2.12890625,324.164062 C246.200156,411.01209 334.125937,456.924851 265.90625,461.902344 C198.734375,466.803385 217.766927,432.829427 323.003906,359.980469" id="Path" stroke="#50E3C2" stroke-width="3"></path>
估计大家都会觉得,要用代码来直接撸,简直就是人间的地狱。那主要是因为我们对每个指令还不是完全的了解或者说理解。既然如此,我们来深入的了解SVG中<path>
元素基本指令的含义与使用姿势。
SVG path基本指令
前面也简单的提到过,要想彻底的了解或者说理解SVG的<path>
元素,就需要对其指令有所了解。那么接下来,咱们就来了解一下其基本指令。
M/m
M
也就是起始点,因此所有path
一定是从M
开始,M
只有两个参数:x
和y
,下列代码表示(0,0)
为起始点,但因为是起始点,所以看不到东西是正常的(可以使用Sketch的钢笔工具点一个点,是看不到东西的)。
<path d="M0 0" stroke="black"/>
H/h
H
可以从当前坐标点画水平线到x
轴的某个坐标点,其只有一个参数,x
的值越大越往左,数字越小越往右。(小写则可视为长度),下面的代码表示画了一条(0,0)
到(50,0)
水平直线。
<path d="M0 0 H50" stroke="black"/>
V/v
V
可以从当前的点画垂直直线到y
轴某个坐标点,其只有一个参数,y
的值越大越往下,数字越小越往上。下面的代码表示画了一条(0,0)
到(0, 50)
垂直直线。
<path d="M0 0 V50" stroke="black"/>
L/l
L
可以从当前点绘制垂直线到某个坐标,其有两个参数x
和y
,也就是要移动到的坐标点。下面的代码表示绘制了一条(0, 0)
至(50, 50)
的直线。
<path d="M0 0 L50 50" stroke="black"/>
C/c
C
表示可以画一个如下图所示的三次贝塞尔曲线(有关于三次贝塞尔曲线的相关介绍,可以点击这里进行了解),因此共有六个控制点,分别是x1
、y1
、x2
、y2
、x
和y
,其中x1, y1
表示三次贝塞尔曲线的第一个控制点,x2, y2
表示三次贝塞尔曲线的第二个控制点,x, y
则表示三次贝塞尔曲线线段的结束点。下面的代码将会绘制一条波浪形的线段。
<path d="M0 0 C40 40,60 40,100,0" stroke="black" fill="none"/>
S/s
S
可以在原本的点后方建立一个带有贝塞尔曲线控制点的点,然后原本的点会以同样的曲线斜率镜射一个贝塞遥曲线控制点。从方案上来理解有点绕,还是来看看示例代码:
<path d="M0 0 C40 40,60 40,100,0 S150 -40, 200 0" stroke="black" fill="none"/>
Q/q
Q
相对而言要简单多了,其表达的意思就是起点和终点的贝塞尔曲线共用同一个控制点,只需要有贝塞尔曲线控制点的坐标和终点坐标就可以。比如下面的示例:
<path d="M0 0 Q50 50, 100 0" stroke="black" fill="none"/>
T/t
T
只有一组参数x,y
,表示终点的坐标,所以T
的前方要接上Q
才能画出对应的坐标线。
<path d="M0 0 Q50 50, 100 0 T200 0" stroke="black" fill="none"/>
Z/z
Z
没有任何参数,也是放在最后的,加上Z
的话,表示终点和起点会关闭。
<path d="M0 0 Q50 50, 100 0 T200 0 Z" stroke="black" fill="none"/>
A/a
前面几个指令相对而言较为简单,在path
中A
指令是最为复杂的。那么什么是A
指令呢?从前面的列表中可以得知,A
就是弧形(Arcs),用一句话来说,就是绘制椭圆圆弧(Elliptical Arc)。为什么说A
指令是path
中最为复杂的呢?那是因为A
指令参数非常多,其有七个参数:
rx
:椭圆在x
轴的半径(根据不同的终点换算成比例)ry
:椭圆在y
轴的半径(根据不同的终点换算成比例)x-axis-rotation
:弧线与x
轴的夹角large-arc-flag
:1
表示大角度弧线,0
表示小角度弧线(必须有三个点)sweep-flag
:1
为顺时针方向,0
为逆时针方向x
:终点x
坐标点y
:终点y
坐标点
接下来,咱们用一些简单的示例来阐述path
中的A
指令。先来看一个A
指令绘制出来的弧形。扁长型,x,y
轴半径比例为5:1
,小角度弧线,逆时针方向:
<path d="M0 0 A100 20,0 0 0 50 100" stroke="#000" fill="none"/>
扁长型,x,y
轴半径比例为5:1
,小角度弧线,顺时针方向:
<path d="M0 0 A100 20,0 0 1 50 100" stroke="#000" fill="none"/>
扁长型,x,y
转半径比例为5:1
,大角度弧线,顺时针方向(因为只有两个点,所以大小角度结果相同,返回原来的点了):
<path d="M0 0 A100 20,0 1 1 50 100" stroke="#000" fill="none"/>
看到上图可能很多人会混淆,为什么大小角度是相同的结果。那是因为path
里头只提供了两个点,两个点只有一条线,不会有角度的问题。为了更好的测试,多添加一个点,就可以看出大小角度的差异:
<!-- 大角度 ( 黑色线 ) --><path d="M0 0 L50 50 A50 50,0 1 0 100 0" stroke="#000" fill="none"/><!-- 小角度 ( 红色线 ) --><path d="M0 0 L50 50 A50 50,0 0 0 100 0" stroke="#f00" fill="none"/>
不过这里要注意。如果弧形的x,y
轴的长度相加,小于弧线两点之间的间距,那么就会一律以大角度弧线显示。就上面的公式来说,弧线两点之间的长度为:50
的平方加50
的平方然后开平方(开根号),大约是70.7
左右,因此如果小于弧线的两个轴的长度相加小于70.71
,就会一律用大角度弧线表示。
<!-- 大角度 ( 黑色线 ) --><path d="M0 0 L50 50 A50 10,0 1 0 100 0" stroke="#000" fill="none"/><!-- 小角度 ( 红色线 ) --><path d="M0 0 L50 50 A50 10,0 0 0 100 0" stroke="#f00" fill="none"/>
小于70.71
(加起来60
,两条弧线重叠)
<!-- 大角度 ( 黑色线 ) --><path d="M0 0 L50 50 A50 30,0 1 0 100 0" stroke="#000" fill="none"/><!-- 小角度 ( 红色线 ) --><path d="M0 0 L50 50 A50 30,0 0 0 100 0" stroke="#f00" fill="none"/>
大于70.71
(加起来80
,两条弧线分开)
为什么会这样呢?主要是因为小角度的弧线,是指三角形的三个内角和小于180
度,但不代表弧线和直线的和可以变成负值,因为如果变成负值,计算起来也会变成大角度的弧线,这也是要非常注意的地方!下面这张图就是很好的阐述:
关于A
的指令,最难的就是上述的大角度小角度弧线,而最后一个参数,就是指弧线跟x
轴的夹角,如下所示:
<!-- 夹角 0 度 ( 黑色线 ) --><path d="M0 0 A50 100,0 0 0 100 0" stroke="#000" fill="none"/><!-- 夹角 30 度 ( 红色线 ) --><path d="M0 0 A50 100,30 0 0 100 0" stroke="#f00" fill="none"/><!-- 夹角 60 度 ( 橘色线 ) --><path d="M0 0 A50 100,60 0 0 100 0" stroke="#f90" fill="none"/>
相对坐标指令
与绝对坐标绘制指令的字母一样的,只不过全部是小写表示。这组指令的参数中涉及坐标的参数代表的是相对坐标。也就是说参数代表的是从当前点到目标点的偏移量,正数就代表向轴正向偏移,负值表示向反向仿移。不同的是Z
指令没有大小之分。
不过需要注意的是,绝对坐标指令和相对坐标指令是可以混用的。有时候混用可以带来更灵活的画法。
样式,空格和逗号
你有一定的控制措施来使用空格。你可以删除并添加空白以使每个命令和整个路径数据更晚阅读。
<svg width="600" height="400"><path d="M50 50 v300 h200 v-300 Z" fill="none" stroke="#000" stroke-width="2px" /></svg>
第一个命令之间我用了一个空格符,但是在与下一组命令之间额外添加了一个空格。
虽然在path
命令中,逗号不是必需的,但是可以使用它来分隔任何命令后的x
和y
坐标。在命令之间不使用它们。也就是说,只在坐标之间使用逗号。
<svg width="600" height="400"><path d="M50,50 v300 h200 v-300 Z" fill="none" stroke="#000" stroke-width="2px" /></svg>
你也可以使用更多的空格,并将每个路径命令单独放一行:
<svg width="600" height="400"><path d="M50 50
v300
h200
v-300
Z"
fill="none" stroke="#000" stroke-width="2px" /></svg>
这样可能易于阅读,但显然占用了更多的空间。
直线命令和曲线命令
前面主要了解了path
中的几个命令(指令)。其实这些指令可以分为直线命令和曲线命令。
直线命令
<path>
中有五个画直线的命令,顾名思义,直线命令就是在两个点之间画直线。首先是M
命令。使用两个参数,分别是需要移动到的点的x
轴和y
轴的坐标。假设,你的画笔当前位于一个点,在使用M命令移动画笔后,只会移动画笔,但不会在两点之间画线。因为M
命令仅仅是移动画笔,但不画线。所以M命令经常出现在路径的开始处,用来指明从何处开始画。
能够真正画出线的命令有三个(M
命令是移动画笔位置,但是不画线),最常用的是L
命令,L
需要两个参数,分别是一个点的x
轴和y
轴坐标,L
命令将会在当前位置和新位置(L
前面画笔所在的点)之间画一条线段。
另外还有两个简写命令,用来绘制平行线和垂直线。H
绘制平行线。V
绘制垂直线。这两个命令都只带一个参数,标明在x
轴或y
轴移动到的位置,因为它们都只在坐标轴的一个方向上移动。
现在我们已经掌握了一些命令,可以开始画一些东西了。先从简单的地方开始,画一个简单的矩形(同样的效果用<rect/>
元素可以更简单的实现),矩形是由水平线和垂直线组成的,所以这个例子可以很好地展现前面讲的画线的方法。
<svg width="100px" height="100px"><path d="M10 10 H 90 V 90 H 10 L 10 10"/><!-- Points --><circle cx="10" cy="10" r="2" fill="red"/><circle cx="90" cy="90" r="2" fill="red"/><circle cx="90" cy="10" r="2" fill="red"/><circle cx="10" cy="90" r="2" fill="red"/></svg>
效果如下:
最后,我们可以通过一个“闭合路径命令”Z
来简化上面的path
,Z
命令会从当前点画一条直线到路径的起点,尽管我们不总是需要闭合路径,但是它还是经常被放到路径的最后。另外,Z
命令不用区分大小写。
<path d="M10 10 H 90 V 90 H 10 Z" fill="transparent" stroke="black"/>
你也可以使用这些命令的相对坐标形式来绘制相同的图形,如之前所述,相对命令使用的是小写字母,它们的参数不是指定一个明确的坐标,而是表示相对于它前面的点需要移动多少距离。例如前面的示例,画的是一个80*80
的正方形,用相对命令可以这样描述:
<path d="M10 10 h 80 v 80 h -80 Z" fill="transparent" stroke="black"/>
上述路径是:画笔移动到(10,10)
点,由此开始,向右移动80
像素构成一条水平线,然后向下移动80
像素,然后向左移动80
像素,然后再回到起点。
你可能会问这些命令有什么用,因为 <polygon>
和 <polyline>
可以做到画出一样的图形。答案是,这些命令可以做得更多。如果你只是画直线,那么其他元素可能会更好用,但是,path
却是众多开发者在SVG绘制中经常用到的。据我所知,它们之间不存在性能上的优劣。但是通过脚本生成path
可能有所不同,因为另外两种方法只需要指明点,而path
在这方面的语法会更复杂一些。
曲线命令
绘制平滑曲线的命令有三个,其中两个用来绘制贝塞尔曲线,另外一个用来绘制弧形或者说是圆的一部分。如果你在Inkscape、Illustrator或者Photoshop中用过路径工具,可能对贝塞尔曲线有一定程度的了解。欲了解贝塞尔曲线的完整数学讲解,请阅读这份Wikipedia的文档。在这里不用讲得太多。贝塞尔曲线的类型有很多,但是在path
元素里,只存在两种贝塞尔曲线:三次贝塞尔曲线C
,和二次贝塞尔曲线Q
。
三次贝塞尔曲线需要定义一个点和两个控制点,所以用C
命令创建三次贝塞尔曲线,需要设置三组坐标参数:
C x1 y1, x2 y2, x y
// 或
c dx1 dy1, dx2 dy2, dx dy
这里的最后一个坐标(x,y)
表示的是曲线的终点,另外两个坐标是控制点,(x1,y1)
是起点的控制点,(x2,y2)
是终点的控制点。如果你熟悉代数或者微积分的话,会更容易理解控制点,控制点描述的是曲线起始点的斜率,曲线上各个点的斜率,是从起点斜率到终点斜率的渐变过程。
你可以将若干个贝塞尔曲线连起来,从而创建出一条很长的平滑曲线。通常情况下,一个点某一侧的控制点是它另一侧的控制点的对称(以保持斜率不变)。这样,你可以使用一个简写的贝塞尔曲线命令S
,如下所示:
S x2 y2, x y
// 或
s dx2 dy2, dx dy
S
命令可以用来创建与之前那些曲线一样的贝塞尔曲线,但是,如果S
命令跟在一个C
命令或者另一个S
命令的后面,它的第一个控制点,就会被假设成前一个控制点的对称点。如果S
命令单独使用,前面没有C
命令或者另一个S
命令,那么它的两个控制点就会被假设为同一个点。下面是S
命令的语法示例,右图中的某个控制点用红色标示,与它对称的控制点用蓝色标示。
另一种可用的贝塞尔曲线是二次贝塞尔曲线Q
,它比三次贝塞尔曲线简单,只需要一个控制点,用来确定起点和终点的曲线斜率。因此它需要两组参数,控制点和终点坐标。
Q x1 y1, x y
// 或
q dx1 dy1, dx dy
就像三次贝塞尔曲线有一个S
命令,二次贝塞尔曲线有一个差不多的T
命令,可以通过更简短的参数,延长二次贝塞尔曲线。
T x y (or t dx dy)
和之前一样,快捷命令T
会通过前一个控制点,推断出一个新的控制点。这意味着,在你的第一个控制点后面,可以只定义终点,就创建出一个相当复杂的曲线。需要注意的是,T
命令前面必须是一个Q
命令,或者是另一个T
命令,才能达到这种效果。如果T单独使用,那么控制点就会被认为和终点是同一个点,所以画出来的将是一条直线。
虽然三次贝塞尔曲线拥有更大的自由度,但是两种曲线能达到的效果总是差不多的。具体使用哪种曲线,通常取决于需求,以及对曲线对称性的依赖程度。
弧形命令A
是另一个创建SVG曲线的命令。基本上,弧形可以视为圆形或椭圆形的一部分。假设,已知椭圆形的长轴半径和短轴半径,另外已知两个点(它们的距离在圆的半径范围内),这时我们会发现,有两个路径可以连接这两个点。每种情况都可以生成出四种弧形。
总结
文章开头我们就说过了,SVG中的path
是SVG中的重中之重。对于绘制图形而言,path
是非常强大的,可以通过path
中的相关指令实现上一篇文章中提到的绘制图形元素的功能。这样一来,path
也变得复杂得多。如果你想通过手写path
代码相对而言是较为痛苦的,就算是你理解了path
中所有的指令也较为困难。但值得庆幸的是,我们可以通过Sketch这样的制图软件来绘制。
话又说回来,如果仅了解path
中的指令还是不够的,特别是其中贝塞尔曲和弧线的部分。我们需要花更多的时间来深入的学习。后续我将会整理更多有关于这方面的资料。如果你感兴趣,欢迎继续关注相关的更新。
由于本人是初学者,如果文章中有不对之处,烦请各路大婶多多拍正。如果你在这方面有更多的经验或者更好的建议,欢迎在下面的评论中与我们一起分享。
如需转载,烦请注明出处:https://www.w3cplus.com/svg/svg-path.html