SVG矢量绘图 path路径详解(贝塞尔曲线, 曲线, 平滑直线及弧形)

path元素的形状是通过属性d定义的,属性d的值是一个“命令+参数”的序列,我们将讲解这些可用的命令,并且展示一些示例。

每一个命令都用一个关键字母来表示,比如,字母“M”表示的是“Move to”命令,当解析器读到这个命令时,它就知道你是打算移动到某个点。跟在命令字母后面的,是你需要移动到的那个点的x和y轴坐标。比如移动到 (10,10)这个点的命令,应该写成“M 10 10”。这一段字符结束后,解析器就会去读下一段命令。每一个命令都有两种表示方式,一种是用大写字母,表示采用绝对定位。另一种是用小写字母,表示采用相对定位(例如:从上一个点开始,向上移动10px,向左移动7px)。

因为属性d采用的是用户坐标系统,所以不需标明单位。在后面的教程中,我们会学到如何让变换路径,以满足更多需求。

直线命令

M

【Move to】需要两个参数,分别是需要移动到的点的x轴和y轴的坐标。类似于移动画笔的位置。

M x y //绝对位置
m dx dy //相对位置。后续提到其他命令雷同,故省略不再赘述。

L

【Line to】需要两个参数,分别是一个点的x轴和y轴坐标,L命令将会在当前位置和新位置(L前面画笔所在的点)之间画一条线段。

H 【绘制平行线】

V 【绘制垂直线】

这两个命令都只带一个参数,标明在x轴或y轴移动到的位置,因为它们都只在坐标轴的一个方向上移动

Z 【闭合路径】

Z命令会从当前点画一条直线到路径的起点。不区分大小写

看一下MDN中给的例子:画的是一个矩形。

<?xml version="1.0" standalone="no"?>
<svg width="100px" height="100px" version="1.1" xmlns="http://www.w3.org/2000/svg">

  <path d="M10 10 H 90 V 90 H 10 L 10 10"/>
   <!-- 利用Z命令,效果与上一句相同 -->
  <path d="M10 10 H 90 V 90 H 10 Z"/>

  <!-- 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>

效果图如下:

SVG矢量绘图 path路径详解(贝塞尔曲线及平滑)
SVG矢量绘图 path路径详解(贝塞尔曲线及平滑)

曲线命令

C

三次贝塞尔曲线。(x,y)表示的是曲线的终点,(x1,y1)是起点的控制点,(x2,y2)是终点的控制点。
控制点描述的是曲线起始点的斜率,曲线上各个点的斜率,是从起点斜率到终点斜率的渐变过程。

C x1 y1, x2 y2, x y

有点抽象,可以看一下例子。(建议自己拍一下代码看看效果)

<?xml version="1.0" standalone="no"?>

<svg width="190px" height="160px" version="1.1" xmlns="http://www.w3.org/2000/svg">

  <path d="M10 10 C 20 20, 40 20, 50 10" stroke="black" fill="transparent"/>
  <path d="M70 10 C 70 20, 120 20, 120 10" stroke="black" fill="transparent"/>
  <path d="M130 10 C 120 20, 180 20, 170 10" stroke="black" fill="transparent"/>
  <path d="M10 60 C 20 80, 40 80, 50 60" stroke="black" fill="transparent"/>
  <path d="M70 60 C 70 80, 110 80, 110 60" stroke="black" fill="transparent"/>
  <path d="M130 60 C 120 80, 180 80, 170 60" stroke="black" fill="transparent"/>
  <path d="M10 110 C 20 140, 40 140, 50 110" stroke="black" fill="transparent"/>
  <path d="M70 110 C 70 140, 110 140, 110 110" stroke="black" fill="transparent"/>
  <path d="M130 110 C 120 140, 180 140, 170 110" stroke="black" fill="transparent"/>
</svg>
SVG矢量绘图 path路径详解(贝塞尔曲线及平滑)
SVG矢量绘图 path路径详解(贝塞尔曲线及平滑)
SVG矢量绘图 path路径详解(贝塞尔曲线及平滑)
SVG矢量绘图 path路径详解(贝塞尔曲线及平滑)

S

当一个点某一侧的控制点是它另一侧的控制点的对称(以保持斜率不变),可以使用S命令。简写的贝塞尔曲线命令。
如果S命令跟在一个C命令或者另一个S命令的后面,它的第一个控制点,就会被假设成前一个控制点的对称点。如果S命令单独使用,前面没有C命令或者另一个S命令,那么它的两个控制点就会被假设为同一个点。

S x2 y2, x y

继续扔一个栗子来看看。【注意】蓝色部分是对称的控制点

<svg width="190px" height="160px" version="1.1" xmlns="http://www.w3.org/2000/svg">
  <path d="M10 80 C 40 10, 65 10, 95 80 S 150 150, 180 80" stroke="black" fill="transparent"/>
</svg>
SVG矢量绘图 path路径详解(贝塞尔曲线及平滑)
SVG矢量绘图 path路径详解(贝塞尔曲线及平滑)

Q

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

Q x1 y1, x y

看一下例子,(代码中表示点的位置没写。)

<svg width="190px" height="160px" version="1.1" xmlns="http://www.w3.org/2000/svg"">
  <path d="M10 80 Q 95 10 180 80" stroke="black" fill="transparent"/>
</svg>

效果图,path是黑色曲线

SVG矢量绘图 path路径详解(贝塞尔曲线及平滑)
SVG矢量绘图 path路径详解(贝塞尔曲线及平滑)

T

与S命令相似,是Q命令的简写命令。
与S命令相似,T也会通过前一个控制点,推断出一个新的控制点。这意味着,在你的第一个控制点后面,可以只定义终点,就创建出一个相当复杂的曲线。

【需要注意】,T命令前面必须是一个Q命令,或者是另一个T命令,才能达到这种效果。如果T单独使用,那么控制点就会被认为和终点是同一个点,所以画出来的将是一条直线

T x y

还是看例子比较清楚。

<?xml version="1.0" standalone="no"?>
<svg width="190px" height="160px" version="1.1" xmlns="http://www.w3.org/2000/svg">
  <path d="M10 80 Q 52.5 10, 95 80 T 180 80" stroke="black" fill="transparent"/>
</svg>

效果图如下:【注意蓝色部分是自动补全对称的】

SVG矢量绘图 path路径详解(贝塞尔曲线及平滑)
SVG矢量绘图 path路径详解(贝塞尔曲线及平滑)

维基百科:贝塞尔曲线

以二次贝塞尔曲线的公式为例:

二次贝塞尔曲线公式

js函数:

//p0、p1、p2三个点,其中p0为起点,p2为终点,p1为控制点
//它们的坐标用数组表示[x,y]
//t的范围是0-1
function qBerzier(p0,p1,p2,t){
    var x = (1 - t) * (1 - t) * p0[0] + 2 * t * (1 - t) * p1[0] + t * t * p2[0];
    var y = (1 - t) * (1 - t) * p0[1] + 2 * t * (1 - t) * p1[1] + t * t * p2[1];
    return [x,y];
}

通过函数,可以用L方法绘制贝塞尔曲线,但是这种方法是用直线将离散的点连接起来,只是因为点的距离很近,所以看上去是曲线,不够完美。所以绘制贝塞尔曲线,一般还是用path里的曲线命令比较好。

path路径绘制中,绘制贝塞尔曲线的命令包括:

Q 二次贝赛尔曲线 x1,y1 x,y

T 平滑二次贝塞尔曲线 x,y

C 曲线(curveto) x1,y1 x2,y2 x,y

S 平滑曲线 x2,y2 x,y

其中Q和T可以在一起使用,C和S可以在一起用。

Q 二次贝塞尔曲线

用法:M x0,y0 Q x1,y1 x2,y2

Snip20150708_4

通过M定义(x0,y0)为起点,用Q定义(x1,y1)为控制点,(x2,y2)为终点,构成一条二次贝塞尔曲线。 Q方法非常简单直观。

T 二次贝塞尔曲线平滑延伸

用法:M x0,y0 Q x1,y1 x2,y2 T x4,y4

Snip20150708_5

T命令是二次被赛尔曲线的平滑延伸命令,用Q定义了一段二次贝塞尔曲线后面,紧接着一个T命令,只需定义终点,就可以自动延伸出一条平滑的曲线。 在这段曲线中,(x3,y3)没有手工定义,这里使用的T方法,只定义一个终点,起点是上一段曲线的终点,控制点则是上一段曲线的控制点的对称点(相对于 上一段曲线的终点)。 在这里,(x3,y3)是(x1,y1)的对称点(相对于(x2,y2)对称)。

C 三次贝塞尔曲线

用法:M x0,y0 C x1,y1 x2,y2 x3,y3

Snip20150708_6

C方法定义的是三次贝塞尔曲线,这里只是多了一个控制点,很好理解

S 三次贝塞尔曲线平滑延伸

用法:M x0,y0 C x1,y1 x2,y2 x3,y3 S x5,y5 x6,y6

Snip20150708_7

三次贝塞尔也有一个平滑延伸的命令,需要定义一个控制点和一个终点,命令会再自动生成一个控制点,从而形成一条延伸出来的三次贝赛尔曲线。

弧形

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

弧形命令A的前两个参数分别是x轴半径和y轴半径,它们的作用很明显,不用多做解释,如果你不是很清楚它们的作用,可以参考一下椭圆ellipse命令中的相同参数。弧形命令A的第三个参数表示弧形的旋转情况,下面的例子可以很好地解释它:

SVG矢量绘图 path路径详解(贝塞尔曲线, 曲线, 平滑直线及弧形)
SVG矢量绘图 path路径详解(贝塞尔曲线, 曲线, 平滑直线及弧形)
<svg width="320px" height="320px" version="1.1" xmlns="http://www.w3.org/2000/svg">
  <path d="M10 315
           L 110 215
           A 30 50 0 0 1 162.55 162.45
           L 172.55 152.45
           A 30 50 -45 0 1 215.1 109.9
           L 315 10" stroke="black" fill="green" stroke-width="2" fill-opacity="0.5"/>
</svg>

如图例所示,画布上有一条对角线,中间有两个椭圆弧被对角线切开(x radius = 30, y radius = 50)。第一个椭圆弧的x-axis-rotation(x轴旋转角度)是0,所以弧形所在的椭圆是正置的(没有倾斜)。在第二个椭圆弧中,x- axis-rotation设置为-45,所以这是一个旋转了45度的椭圆,并以短轴为分割线,形成了两个对称的弧形。参看图示中的第二个椭圆形。

上面提到的四种不同路径将由接下来的两个参数决定。如前所讲,还有两种可能的椭圆用来形成路径,它们给出的四种可能的路径中,有两种不同的路径。这 里要讲的参数是large-arc-flag(角度大小) 和sweep-flag(弧线方向),large-arc-flag决定弧线是大于还是小于180度,0表示小角度弧,1表示大角度弧。sweep- flag表示弧线的方向,0表示从起点到终点沿逆时针画弧,1表示从起点到终点沿顺时针画弧。下面的例子展示了这四种情况。

SVG矢量绘图 path路径详解(贝塞尔曲线, 曲线, 平滑直线及弧形)
SVG矢量绘图 path路径详解(贝塞尔曲线, 曲线, 平滑直线及弧形)
<svg width="325px" height="325px" version="1.1" xmlns="http://www.w3.org/2000/svg">
  <path d="M80 80
           A 45 45, 0, 0, 0, 125 125
           L 125 80 Z" fill="green"/>
  <path d="M230 80
           A 45 45, 0, 1, 0, 275 125
           L 275 80 Z" fill="red"/>
  <path d="M80 230
           A 45 45, 0, 0, 1, 125 275
           L 125 230 Z" fill="purple"/>
  <path d="M230 230
           A 45 45, 0, 1, 1, 275 275
           L 275 230 Z" fill="blue"/>
</svg>

你应该已经猜到了,最后两个参数是指定弧形的终点,弧形可以简单地创建圆形或椭圆形图标,比如你可以创建若干片弧形,组成一个饼图

如果你是从Canvas过 渡到SVG,那么弧形会比较难以掌握,但它也是非常强大的。用路径来绘制完整的圆或者椭圆是比较困难的,因为圆上的任意点都可以是起点同时也是终点,无数 种方案可以选择,真正的路径无法定义。通过绘制连续的路径段落,也可以达到近似的效果,但使用真正的circle或者ellipse元素会更容易一些。

 

更多参考:

SVG动画入门

SVG动画入门(二)

SVG技术入门:线条动画实现原理

如何使用CSS来修改SVG原点和制作SVG动画

纯CSS实现帅气的SVG路径描边动画效果

SVG矢量绘图 path路径详解(基本画法)

SVG矢量绘图 path路径详解(贝塞尔曲线及平滑)

Web流程图绘制使用raphael

本文:SVG矢量绘图 path路径详解(贝塞尔曲线及平滑)

 

One Comment

Leave a Reply