路径

<path> 元素是 SVG 基本形状库中最强大的元素。它可以用于创建直线、曲线、弧线等。

路径通过组合多条直线或曲线来创建复杂形状。仅由直线组成的复杂形状可以作为 <polyline> 元素创建。虽然 <polyline><path> 元素可以创建相似的形状,但 <polyline> 元素需要大量的小直线来模拟曲线,并且在放大到更大尺寸时缩放效果不佳。

在绘制 SVG 时,对路径的良好理解非常重要。虽然不建议使用 XML 编辑器或文本编辑器创建复杂的路径,但了解它们的工作原理将有助于识别和修复 SVG 中的显示问题。

<path> 元素的形状由一个参数定义:d。(更多信息请参见基本形状。)d 属性包含一系列命令和这些命令使用的参数。

每个命令都由一个特定的字母实例化(例如,创建类、命名和定位)。例如,让我们移动到 x 和 y 坐标 (10, 10)。“移动到”命令用字母 M 调用。当解析器遇到这个字母时,它就知道需要移动到一个点。因此,要移动到 (10, 10),要使用的命令是 M 10 10。之后,解析器开始读取下一个命令。

所有命令也都有两种变体。大写字母指定页面上的绝对坐标,小写字母指定相对坐标(例如,*从上一个点向上移动 10px,向左移动 7px*)。

d 参数中的坐标始终没有单位,因此在用户坐标系中。稍后,我们将学习如何转换路径以适应其他需求。

直线命令

<path> 节点有五个直线命令。第一个命令是“移动到”或 M,上面已描述过。它接受两个参数,一个 x 坐标 (x) 和一个 y 坐标 (y) 以移动到。如果光标已经在页面上的某个位置,则不会绘制线条来连接这两个位置。“移动到”命令出现在路径的开头,用于指定绘图的起始位置。例如:

M x y
(or)
m dx dy

在下面的例子中,只有一个点在 (10, 10)。但是请注意,如果路径只是正常绘制,它不会显示。例如:

html
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
  <path d="M10 10" />
</svg>

有三个命令用于绘制线条。最通用的是“画线到”命令,用 L 调用。L 接受两个参数——x 和 y 坐标——并从当前位置绘制一条线到新位置。

L x y
(or)
l dx dy

有两种缩写形式用于绘制水平线和垂直线。H 绘制水平线,V 绘制垂直线。这两个命令都只接受一个参数,因为它们只沿一个方向移动。

H x
(or)
h dx

V y
(or)
v dy

一个简单的开始是绘制一个形状。我们将从一个矩形开始(与使用 <rect> 元素可以更轻松地创建的矩形类型相同)。它只由水平线和垂直线组成。

html
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
  <path d="M 10 10 H 90 V 90 H 10 L 10 10" />
</svg>

我们可以通过使用“闭合路径”命令(用 Z 调用)来稍微缩短上面的路径声明。此命令从当前位置绘制一条直线回到第一个未闭合点(如果存在,则为最后一个 Z 命令后的第一个点,否则为路径中的第一个点),并以线连接闭合路径。它通常放在路径节点的末尾,但并非总是如此。大写和小写命令之间没有区别。

Z
(or)
z

所以我们上面的路径可以缩短为

xml
<path d="M 10 10 H 90 V 90 H 10 Z" fill="transparent" stroke="black" />

这些命令的相对形式也可以用来绘制相同的图片。相对命令通过使用小写字母调用,它们不是将光标移动到精确的坐标,而是相对于其最后位置移动。例如,由于我们的矩形是 80×80,<path> 元素可以写成

xml
<path d="M 10 10 h 80 v 80 h -80 Z" fill="transparent" stroke="black" />

路径将移动到点 (10, 10),然后向右水平移动 80 个点,然后向下 80 个点,然后向左 80 个点,然后返回到起点。

在这些示例中,使用 <polygon><polyline> 元素可能更直观。然而,路径在 SVG 绘图中经常使用,开发者可能更习惯使用它们。使用其中一个并没有真正的性能损失或增益。

曲线命令

有三种不同的命令可以用来创建平滑曲线。其中两种曲线是 贝塞尔曲线,第三种是“弧线”或圆形的一部分。您可能已经在使用 Inkscape、Illustrator 或 Photoshop 中的路径工具获得了贝塞尔曲线的实践经验。贝塞尔曲线的数量是无限的,但 <path> 元素中只有两种:一种是三次曲线,用 C 调用,另一种是二次曲线,用 Q 调用。

贝塞尔曲线

三次曲线 C 是一种稍微复杂的曲线。三次贝塞尔曲线为每个点接受两个控制点。因此,要创建三次贝塞尔曲线,需要指定三组坐标。

C x1 y1, x2 y2, x y
(or)
c dx1 dy1, dx2 dy2, dx dy

最后一组坐标 (x, y) 指定线条应在哪里结束。另外两个是控制点。(x1, y1) 是曲线起点的控制点,(x2, y2) 是曲线终点的控制点。控制点本质上描述了从每个点开始的线的斜率。然后,贝塞尔函数创建一条平滑曲线,从线起点建立的斜率过渡到另一端的斜率。

html
<svg width="190" height="160" xmlns="http://www.w3.org/2000/svg">
  <path d="M 10 10 C 20 20, 40 20, 50 10" stroke="black" fill="transparent" />
  <path d="M 70 10 C 70 20, 110 20, 110 10" stroke="black" fill="transparent" />
  <path
    d="M 130 10 C 120 20, 180 20, 170 10"
    stroke="black"
    fill="transparent" />
  <path d="M 10 60 C 20 80, 40 80, 50 60" stroke="black" fill="transparent" />
  <path d="M 70 60 C 70 80, 110 80, 110 60" stroke="black" fill="transparent" />
  <path
    d="M 130 60 C 120 80, 180 80, 170 60"
    stroke="black"
    fill="transparent" />
  <path
    d="M 10 110 C 20 140, 40 140, 50 110"
    stroke="black"
    fill="transparent" />
  <path
    d="M 70 110 C 70 140, 110 140, 110 110"
    stroke="black"
    fill="transparent" />
  <path
    d="M 130 110 C 120 140, 180 140, 170 110"
    stroke="black"
    fill="transparent" />
</svg>

上面的例子创建了九条三次贝塞尔曲线。随着曲线向右移动,控制点在水平方向上散开。随着曲线向下移动,它们与端点之间的距离变得更远。这里需要注意的是,曲线沿第一个控制点的方向开始,然后弯曲,使其沿第二个控制点的方向到达。

可以将多条贝塞尔曲线串联起来,以创建扩展的平滑形状。通常,点一侧的控制点将是另一侧使用的控制点的反射,以保持斜率恒定。在这种情况下,可以使用三次贝塞尔曲线的快捷版本,用命令 S(或 s)表示。

S x2 y2, x y
(or)
s dx2 dy2, dx dy

S 生成与之前相同类型的曲线——但如果它跟随另一个 S 命令或 C 命令,则假定第一个控制点是先前使用的控制点的反射。如果 S 命令不跟随另一个 SC 命令,则将光标的当前位置用作第一个控制点。结果与 Q 命令使用相同参数生成的结果不同,但相似。

下面显示了这种语法的一个示例,左图中红色显示了指定的控制点,蓝色显示了推断的控制点。

html
<svg width="190" height="160" xmlns="http://www.w3.org/2000/svg">
  <path
    d="M 10 80 C 40 10, 65 10, 95 80 S 150 150, 180 80"
    stroke="black"
    fill="transparent" />
</svg>

另一种贝塞尔曲线,用 Q 调用的二次曲线,实际上比三次曲线更简单。它需要一个控制点,该控制点确定曲线在起点和终点的斜率。它接受两个参数:控制点和曲线的终点。

注意: q 的坐标增量都相对于前一个点(也就是说,dxdy 不相对于 dx1dy1)。

Q x1 y1, x y
(or)
q dx1 dy1, dx dy
html
<svg width="190" height="160" xmlns="http://www.w3.org/2000/svg">
  <path d="M 10 80 Q 95 10 180 80" stroke="black" fill="transparent" />
</svg>

与三次贝塞尔曲线一样,有一个快捷方式用于将多个二次贝塞尔曲线串联起来,用 T 调用。

T x y
(or)
t dx dy

此快捷方式查看先前使用的控制点并从中推断出一个新控制点。这意味着在第一个控制点之后,可以通过仅指定终点来创建相当复杂的形状。

这仅在之前的命令是 QT 命令时有效。否则,控制点被假定与前一个点相同,并且只会绘制线条。

html
<svg width="190" height="160" xmlns="http://www.w3.org/2000/svg">
  <path
    d="M 10 80 Q 52.5 10, 95 80 T 180 80"
    stroke="black"
    fill="transparent" />
</svg>

两种曲线都会产生相似的结果,尽管三次曲线在曲线的具体外观上提供了更大的自由度。决定使用哪种曲线取决于具体情况以及线条的对称性。

弧线

另一种可以使用 SVG 创建的曲线是弧线,通过 A 命令调用。弧线是圆形或椭圆的一部分。

对于给定的 x 半径和 y 半径,可以连接任意两个点的椭圆有两个(只要它们在圆的半径内)。沿着这两个圆中的任何一个,有两条可能的路径可以连接这些点——因此在任何情况下,都有四条可能的弧线可用。

正因为如此,弧线需要相当多的参数

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

在开始时,弧线元素接受 x 半径和 y 半径的两个参数。如果需要,请参阅 <ellipse> 及其行为。最后两个参数指定绘制终点的 x 和 y 坐标。这四个值共同定义了弧线的基本结构。

第三个参数描述了弧线的旋转。这最好通过一个例子来解释

html
<svg width="320" height="320" xmlns="http://www.w3.org/2000/svg">
  <path
    d="M 10 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>

该示例显示一个对角线穿过页面的 <path> 元素。在其中心,有两个椭圆形弧线被切出(x 半径 = 30,y 半径 = 50)。在第一个中,x 轴旋转保持为 0,因此弧线围绕的椭圆(显示为灰色)是垂直方向的。然而,对于第二个弧线,x 轴旋转设置为 -45 度。这旋转了椭圆,使其与路径方向的小轴对齐,如示例图像中的第二个椭圆所示。

对于上图中未旋转的椭圆,只有两个不同的弧线,而不是四个可供选择,因为从弧线起点到终点绘制的线穿过椭圆的中心。在一个稍作修改的例子中,可以看到形成四个不同弧线的两个椭圆

html
<svg xmlns="http://www.w3.org/2000/svg" width="320" height="320">
  <path
    d="M 10 315
       L 110 215
       A 36 60 0 0 1 150.71 170.29
       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>

请注意,每个蓝色椭圆都由两个弧线形成,具体取决于顺时针或逆时针方向。每个椭圆都有一条短弧线和一条长弧线。这两个椭圆只是彼此的镜像。它们沿着从起点到终点形成的线翻转。

如果起点到终点之间的距离超出了椭圆的 xy 半径所能达到的范围,则椭圆的半径将被最小程度地扩展,以便它能够到达起点到终点。此页面底部的交互式代码笔很好地演示了这一点。要确定椭圆的半径是否足够大以需要扩展,需要解决一个方程组,例如 Wolfram Alpha 上的这个。此计算用于未旋转的椭圆,起点到终点为 (110, 215) → (150.71, 170.29)。解 (x, y) 是椭圆的中心。如果椭圆的半径太小,解将是 虚数。第二次计算用于未旋转的椭圆,起点到终点为 (110, 215) → (162.55, 162.45)。解有一个小的虚数分量,因为椭圆刚好被扩展了。

上面提到的四种不同的路径由接下来的两个参数标志决定。如前所述,仍然有两个可能的椭圆可供路径围绕,并且在两个椭圆上都有两条不同的可能路径,总共有四条可能的路径。第一个参数是 large-arc-flag。它决定弧线应大于还是小于 180 度;最终,这个标志决定了弧线将围绕给定圆的哪个方向行驶。第二个参数是 sweep-flag。它决定弧线应该以正角度还是负角度开始移动,这实际上选择将围绕两个圆中的哪一个行驶。下面的示例显示了所有四种可能的组合,以及每种情况的两个圆。

html
<svg width="360" height="360" xmlns="http://www.w3.org/2000/svg">
  <path
    d="M 100 100
       A 45 45, 0, 0, 0, 145 145
       L 145 100 Z"
    fill="#00FF00A0"
    stroke="black"
    stroke-width="2" />
  <path
    d="M 250 100
       A 45 45, 0, 1, 0, 295 145
       L 295 100 Z"
    fill="#FF0000A0"
    stroke="black"
    stroke-width="2" />
  <path
    d="M 100 250
       A 45 45, 0, 0, 1, 145 295
       L 145 250 Z"
    fill="#FF00FFA0"
    stroke="black"
    stroke-width="2" />
  <path
    d="M 250 250
       A 45 45, 0, 1, 1, 295 295
       L 295 250 Z"
    fill="#0000FFA0"
    stroke="black"
    stroke-width="2" />
  <path
    d="M 45 45 L 345 45 L 345 345 L 45 345 Z M 195 45 L 195 345 M 45 195 L 345 195"
    fill="none"
    stroke="black" />
  <text x="140" y="20" font-size="20" fill="black">Large arc flag</text>
  <text
    x="-15"
    y="195"
    font-size="20"
    fill="black"
    transform="rotate(-90)"
    transform-origin="20 195">
    Sweep flag
  </text>
  <text x="120" y="40" font-size="20" fill="black">0</text>
  <text x="270" y="40" font-size="20" fill="black">1</text>
  <text x="30" y="120" font-size="20" fill="black">0</text>
  <text x="30" y="270" font-size="20" fill="black">1</text>
</svg>

弧线是创建图形中圆形或椭圆形部分的简单方法。例如,饼图的每个部分都需要不同的弧线。

如果从 <canvas> 切换到 SVG,弧线可能是最难学习的部分,但它们也更强大。完整的圆形和椭圆是 SVG 弧线难以绘制的唯一形状。因为围绕圆的任何路径的起点和终点是同一个点,所以可以选择的圆的数量是无限的,并且实际路径是未定义的。可以通过使路径的起点和终点稍微偏斜,然后用另一个路径段连接它们来近似它们。例如,可以通过为每个半圆使用一个弧线来制作一个圆。在这一点上,通常更容易使用真正的 <circle><ellipse> 节点。这个交互式演示可能有助于理解 SVG 弧线背后的概念。