SVG之旅:路径

通过上一节的学习,我们知道了怎么在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 y1x2, 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制图软件来举例:

制作图软件中的path

将上图导出为一个.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命令,当解析器读到这个命令时,它就知道你是打算移动到某个点。跟在命令字母后面的,是你需要移动到的那个点的xy轴坐标。比如移动到(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只有两个参数:xy,下列代码表示(0,0)为起始点,但因为是起始点,所以看不到东西是正常的(可以使用Sketch的钢笔工具点一个点,是看不到东西的)。

<path d="M0 0" stroke="black"/>

M/m

H/h

H可以从当前坐标点画水平线到x轴的某个坐标点,其只有一个参数,x的值越大越往左,数字越小越往右。(小写则可视为长度),下面的代码表示画了一条(0,0)(50,0)水平直线。

<path d="M0 0 H50" stroke="black"/>

H/h

V/v

V可以从当前的点画垂直直线到y轴某个坐标点,其只有一个参数,y的值越大越往下,数字越小越往上。下面的代码表示画了一条(0,0)(0, 50)垂直直线。

<path d="M0 0 V50" stroke="black"/>

V/v

L/l

L可以从当前点绘制垂直线到某个坐标,其有两个参数xy,也就是要移动到的坐标点。下面的代码表示绘制了一条(0, 0)(50, 50)的直线。

<path d="M0 0 L50 50" stroke="black"/>

L/l

C/c

C表示可以画一个如下图所示的三次贝塞尔曲线(有关于三次贝塞尔曲线的相关介绍,可以点击这里进行了解),因此共有六个控制点,分别是x1y1x2y2xy,其中x1, y1表示三次贝塞尔曲线的第一个控制点,x2, y2表示三次贝塞尔曲线的第二个控制点,x, y则表示三次贝塞尔曲线线段的结束点。下面的代码将会绘制一条波浪形的线段。

<path d="M0 0 C40 40,60 40,100,0" stroke="black" fill="none"/>

C/c

S/s

S可以在原本的点后方建立一个带有贝塞尔曲线控制点的点,然后原本的点会以同样的曲线斜率镜射一个贝塞遥曲线控制点。从方案上来理解有点绕,还是来看看示例代码:

<path d="M0 0 C40 40,60 40,100,0 S150 -40, 200 0" stroke="black" fill="none"/>

S/s

Q/q

Q相对而言要简单多了,其表达的意思就是起点和终点的贝塞尔曲线共用同一个控制点,只需要有贝塞尔曲线控制点的坐标和终点坐标就可以。比如下面的示例:

<path d="M0 0 Q50 50, 100 0" stroke="black" fill="none"/>

Q/q

T/t

T只有一组参数x,y,表示终点的坐标,所以T的前方要接上Q才能画出对应的坐标线。

<path d="M0 0 Q50 50, 100 0 T200 0" stroke="black" fill="none"/>

T/t

Z/z

Z没有任何参数,也是放在最后的,加上Z的话,表示终点和起点会关闭。

<path d="M0 0 Q50 50, 100 0 T200 0 Z" stroke="black" fill="none"/>

Z/z

A/a

前面几个指令相对而言较为简单,在pathA指令是最为复杂的。那么什么是A指令呢?从前面的列表中可以得知,A就是弧形(Arcs),用一句话来说,就是绘制椭圆圆弧(Elliptical Arc)。为什么说A指令是path中最为复杂的呢?那是因为A指令参数非常多,其有七个参数:

  • rx:椭圆在x轴的半径(根据不同的终点换算成比例)
  • ry:椭圆在y轴的半径(根据不同的终点换算成比例)
  • x-axis-rotation:弧线与x轴的夹角
  • large-arc-flag1表示大角度弧线,0表示小角度弧线(必须有三个点)
  • sweep-flag1为顺时针方向,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"/>

A/a

扁长型,x,y轴半径比例为5:1,小角度弧线,顺时针方向:

<path d="M0 0 A100 20,0 0 1 50 100" stroke="#000" fill="none"/>

A/a

扁长型,x,y转半径比例为5:1,大角度弧线,顺时针方向(因为只有两个点,所以大小角度结果相同,返回原来的点了):

<path d="M0 0 A100 20,0 1 1 50 100" stroke="#000" fill="none"/>

A/a

看到上图可能很多人会混淆,为什么大小角度是相同的结果。那是因为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"/>

A/a

不过这里要注意。如果弧形的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"/>

A/a

小于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"/>

A/a

大于70.71(加起来80,两条弧线分开)

为什么会这样呢?主要是因为小角度的弧线,是指三角形的三个内角和小于180度,但不代表弧线和直线的和可以变成负值,因为如果变成负值,计算起来也会变成大角度的弧线,这也是要非常注意的地方!下面这张图就是很好的阐述:

A/a

关于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"/>

A/a

相对坐标指令

与绝对坐标绘制指令的字母一样的,只不过全部是小写表示。这组指令的参数中涉及坐标的参数代表的是相对坐标。也就是说参数代表的是从当前点到目标点的偏移量,正数就代表向轴正向偏移,负值表示向反向仿移。不同的是Z指令没有大小之分。

不过需要注意的是,绝对坐标指令和相对坐标指令是可以混用的。有时候混用可以带来更灵活的画法。

样式,空格和逗号

你有一定的控制措施来使用空格。你可以删除并添加空白以使每个命令和整个路径数据更晚阅读。

<svg width="600" height="400">
    <path d="M50 50  v300  h200  v-300  Z" fill="none" stroke="#000" stroke-width="2px" />
</svg>

第一个命令之间我用了一个空格符,但是在与下一组命令之间额外添加了一个空格。

虽然在path命令中,逗号不是必需的,但是可以使用它来分隔任何命令后的xy坐标。在命令之间不使用它们。也就是说,只在坐标之间使用逗号

<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来简化上面的pathZ命令会从当前点画一条直线到路径的起点,尽管我们不总是需要闭合路径,但是它还是经常被放到路径的最后。另外,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命令的语法示例,右图中的某个控制点用红色标示,与它对称的控制点用蓝色标示。

S/s

另一种可用的贝塞尔曲线是二次贝塞尔曲线Q,它比三次贝塞尔曲线简单,只需要一个控制点,用来确定起点和终点的曲线斜率。因此它需要两组参数,控制点和终点坐标。

Q x1 y1, x y 

// 或
q dx1 dy1, dx dy

Q/q

就像三次贝塞尔曲线有一个S命令,二次贝塞尔曲线有一个差不多的T命令,可以通过更简短的参数,延长二次贝塞尔曲线。

T x y (or t dx dy)

和之前一样,快捷命令T会通过前一个控制点,推断出一个新的控制点。这意味着,在你的第一个控制点后面,可以只定义终点,就创建出一个相当复杂的曲线。需要注意的是,T命令前面必须是一个Q命令,或者是另一个T命令,才能达到这种效果。如果T单独使用,那么控制点就会被认为和终点是同一个点,所以画出来的将是一条直线。

T/t

虽然三次贝塞尔曲线拥有更大的自由度,但是两种曲线能达到的效果总是差不多的。具体使用哪种曲线,通常取决于需求,以及对曲线对称性的依赖程度。

弧形命令A是另一个创建SVG曲线的命令。基本上,弧形可以视为圆形或椭圆形的一部分。假设,已知椭圆形的长轴半径和短轴半径,另外已知两个点(它们的距离在圆的半径范围内),这时我们会发现,有两个路径可以连接这两个点。每种情况都可以生成出四种弧形。

总结

文章开头我们就说过了,SVG中的path是SVG中的重中之重。对于绘制图形而言,path是非常强大的,可以通过path中的相关指令实现上一篇文章中提到的绘制图形元素的功能。这样一来,path也变得复杂得多。如果你想通过手写path代码相对而言是较为痛苦的,就算是你理解了path中所有的指令也较为困难。但值得庆幸的是,我们可以通过Sketch这样的制图软件来绘制。

话又说回来,如果仅了解path中的指令还是不够的,特别是其中贝塞尔曲和弧线的部分。我们需要花更多的时间来深入的学习。后续我将会整理更多有关于这方面的资料。如果你感兴趣,欢迎继续关注相关的更新。

由于本人是初学者,如果文章中有不对之处,烦请各路大婶多多拍正。如果你在这方面有更多的经验或者更好的建议,欢迎在下面的评论中与我们一起分享。

上一节 下一节

大漠

常用昵称“大漠”,W3CPlus创始人,目前就职于手淘。对HTML5、CSS3和Sass等前端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《图解CSS3:核心技术与案例实战》。

如需转载,烦请注明出处:https://www.w3cplus.com/svg/svg-path.html

返回顶部