深入解析SVG路径元素:曲线与圆弧命令

本文详细解析SVG路径元素中的曲线与圆弧命令,包括二次贝塞尔曲线(Q/T)、三次贝塞尔曲线(C/S)和圆弧命令(A)的语法规则、控制点机制以及实际应用示例,帮助开发者掌握手动编码矢量图形的核心技巧。

解码SVG路径元素:曲线与圆弧命令

在解码SVG路径系列的第一部分中,我们主要处理了将语义标签(line、polyline、polygon)转换为路径命令语法的问题,但路径元素并没有真正提供任何新的形状选项。本文将改变这一状况,我们将学习如何绘制曲线和圆弧——它们只是椭圆的一部分。

快速回顾

如果您是第一次接触本系列,建议先熟悉手动编码SVG的基础知识,了解<marker>的工作原理,并对animate有基本理解。同时推荐掌握<path>的d属性中的M/m命令。

在开始之前,我想快速回顾一下我编码SVG的方式——使用JavaScript。我不喜欢处理数字和数学,阅读每个属性都填满数字的SVG代码会让我完全无法理解。通过给坐标命名,并将所有数学计算清晰写出,我能更好地处理这类代码,相信您也会如此。

由于本文的目标是理解路径语法,而非布局或利用循环等更基础的内容,我不会带您完成每个示例的完整设置。我会分享一些代码片段,但请注意,这些代码可能经过调整或简化以便阅读。如果有关于CodePen演示中未包含的代码的具体问题,评论区始终开放。

为保持框架无关性,代码使用原生JavaScript编写,但在实践中,处理复杂图像时强烈推荐使用TypeScript。

绘制贝塞尔曲线

能够绘制线条、多边形、折线及其复合版本固然有趣,但路径元素还能提供更多功能,而不仅仅是基本语义SVG标签的更隐秘实现。

其中之一就是贝塞尔曲线。

有多种不同的曲线命令,这正是点和控制点概念的用武之地。

贝塞尔数学绘图超出了本文的范围。但Freya Holmér有一个视觉华丽的视频《贝塞尔曲线之美》,深入探讨了三次和二次贝塞尔曲线的构造,配有精美动画,让数学更易理解。

幸运的是,SVG允许我们绘制带有一个控制点的二次曲线和带有两个控制点的三次曲线,而无需进行任何额外的数学计算。

那么,什么是控制点?控制点是控制曲线的手柄位置,它不是一个被绘制的点。

我发现理解这些路径命令的最佳方式是像Affinity和Illustrator等GUI那样渲染它们。然后,绘制“手柄”和一些具有不同属性的随机曲线,观察它们如何影响曲线。观看动画也有助于理解这些命令的机制。

在接下来的视觉效果中,我将使用标记和动画。您会注意到我使用的标记是矩形和圆形,由于它们与线条相连,我可以利用marker元素,从而节省大量动画时间,因为这些附加元素已与系统连接。(而且动画化单个d命令而不是分别动画化x和y属性也使SVG代码更短。)

二次贝塞尔曲线:Q和T命令

Q命令用于绘制二次贝塞尔曲线。它接受两个参数:控制点和终点。

因此,对于一个简单的曲线,我们首先使用M移动到起点,然后使用Q绘制曲线。

1
const path = `M${start.x} ${start.y} Q${control.x} ${control.y} ${end.x} ${end.y}`;

由于我们有控制点、起点和终点,实际上很容易像图形程序那样渲染单一的手柄路径。

有趣的是,在大多数常见的GUI中,您可能从未像与三次曲线那样与二次贝塞尔曲线交互过!大多数常见程序在您想要操作时都会将此曲线转换为带有两个手柄和控制点的三次曲线。

为了绘图,我创建了几个标记,并将手柄绘制为红色以使其更突出。

我还用渐变描边了主路径,并赋予它交叉阴影图案填充。(我们在第一篇文章中讨论了pattern,linearGradient非常相似。它们都是可以通过id引用的def元素。)我喜欢看到填充,但如果您觉得分散注意力,可以修改变量。

我鼓励您查看带有和不带有手柄渲染的示例,以观察控制点靠近它们时点周围发生的一些细微变化。

二次贝塞尔曲线是“弯曲度较小”的曲线。这些曲线总是保持与“u”或“n”形状有些相关,无法被操纵成扭曲形状。但它们可以被挤压。

连接的贝塞尔曲线称为“样条”。在链接多个二次曲线时,有一个附加命令,即T命令。

T命令用于绘制连接到前一个曲线的曲线,因此它必须跟在Q命令(或另一个T命令)之后。它只接受一个参数,即曲线的终点。

1
const path = `M${p1.x} ${p1.y} Q${cP.x} ${cP.y} ${p2.x} ${p2.y} T${p3.x} ${p3.y}`

T命令实际上会使用Q命令中控制点cP的信息。

查看我如何创建以下示例。注意推断的手柄用绿色绘制,而我们指定的控制点仍然用红色渲染。

好的,所以顶部曲线使用两个Q命令,这意味着总共有三个控制点。使用单独的控制点创建扇形是有意义的,但第三个控制点只是第二个控制点通过前一个点的反射。

这就是T命令的作用。它通过前一个Q(或T)命令的终点反射控制点来推断控制点。您可以在下面的动画中看到系统如何连接,其中我仅操纵了主点和第一个控制点的位置。推断的控制点随之移动。

q和t命令也存在,因此它们将使用相对坐标。

在我继续之前,如果您确实想与三次曲线交互,SVG Path Editor允许您很好地编辑所有路径命令。

三次贝塞尔曲线:C和S

三次贝塞尔曲线的工作原理基本与二次曲线类似,但不是有一个控制点,而是有两个。这可能是您最熟悉的曲线。

顺序是:首先第一个控制点,然后第二个,最后是终点。

1
const path = `M${p1.x} ${p1.y} C${cP1.x} ${cP1.y} ${cP2.x} ${cP2.y} ${p2.x} ${p2.y}`;

让我们看一个视觉效果来了解它的实际应用。

三次贝塞尔曲线是柔术师。与二次曲线不同,它可以卷曲形成循环,并呈现与任何其他SVG元素完全不同的形状。它可以将填充区域分成两部分,而二次曲线不能。

就像T命令一样,三次曲线也有一个反射命令S。

使用它时,我们通过反射获得第一个控制点,同时可以定义新的结束控制点和终点。和之前一样,这需要一个样条,因此至少有一个前面的C(或S)命令。

1
2
3
4
5
const path = `    
  M ${p0.x} ${p0.y}
  C ${c0.x} ${c0.y} ${c1.x} ${c1.y} ${p1.x} ${p1.y}
  S ${c2.x} ${c2.y} ${p2.x} ${p2.y}
`;

我也为此创建了一个生动的视觉效果。

何时使用T和S:使用这些链式反射命令的最大优势是如果您想绘制波浪或只是绝对确保样条连接平滑。

如果您不能使用反射但想要有一个良好平滑的连接,请确保您的控制点形成一条直线。如果手柄有扭结,您的样条也会出现扭结。

圆弧:A命令

最后,最后一种路径命令是创建圆弧。圆弧是圆或椭圆的一部分。

这是我最不喜欢的命令,因为它有太多元素。但它是绘制正确圆环图的秘诀,所以我花了一些时间掌握它。

让我们来看看它。

与任何其他路径命令一样,小写表示相对坐标。因此,就像有A命令一样,也有a命令。

所以,一个圆弧路径看起来像这样:

1
const path = `M${start.x} ${start.y} A${radius.x} ${radius.y} ${xAxisRotation} ${largeArcFlag} ${sweepFlag} ${end.x} ${end.y}`;

那么xAxisRotation、largeArcFlag和sweepFlag到底是什么?简而言之:

  • xAxisRotation是底层椭圆轴的旋转角度(度)。
  • largeArcFlag是一个布尔值,决定圆弧是否大于180°。
  • sweepFlag也是一个布尔值,决定圆弧方向,即顺时针还是逆时针。

为了更好地理解这些概念,我创建了这个视觉效果。

半径大小

您会注意到在那个CodePen中,每个命令都绘制了椭圆。在顶行,它们重叠,而在底行,它们堆叠起来。两行实际上在其圆弧定义中使用了相同的radius.x和radius.y值,而第二行的起点和终点之间的距离增加了。

发生堆叠的原因是,只有当起点和终点适合指定椭圆时,半径大小才会被考虑。这种行为让我感到惊讶,因此我深入研究规范,找到了关于圆弧工作原理的以下信息:

“所有椭圆弧参数(除了布尔标志)都允许任意数值,但用户代理在渲染曲线或计算其几何时必须对无效值进行以下调整:

如果段的端点(x, y)与当前点相同(例如,前一个段的端点),则这相当于完全省略椭圆弧段。 如果rx或ry为0,则此弧被视为连接端点的直线段(“lineto”)。 如果rx或ry有负号,则去掉负号;使用绝对值。 如果rx、ry和x-axis-rotation使得没有解(基本上,椭圆不够大,无法从当前点到达新端点),则椭圆均匀缩放,直到恰好有一个解(直到椭圆刚好足够大)。 有关此缩放操作的数学公式,请参阅附录部分“超出范围半径的校正”。

——9.5.1 超出范围的椭圆弧参数

因此,实际上,那种堆叠只是优雅的错误处理,而不是其本意。因为顶行才是圆弧应该使用的方式。

当插入逻辑值时,底层椭圆和两个点为我们提供了四种绘制选项,用于如何沿着椭圆路径连接两个点。这就是布尔值的用途。

xAxisRotation

在我们讨论布尔值之前,交叉阴影图案显示了xAxisRotation。椭圆围绕其中心旋转,度数值相对于SVG的x方向。

因此,如果您使用圆形椭圆,旋转不会对圆弧产生任何影响(除非您像我那样在图案中使用它)。

扫描标志(Sweep Flag)

注意显示圆弧绘制方向的小箭头标记。如果值为0,圆弧顺时针绘制。如果值为1,圆弧逆时针绘制。

大弧标志(Large Arc Flag)

大弧标志告诉路径您是否想要椭圆的较小弧还是较大弧。如果我们有一个缩放的情况,我们正好得到椭圆的180°。

圆弧通常需要比我乐意做的更烦人的循环数字处理(一旦弧度出现,我往往会陷入必须重新学习太多我乐意忘记的数学的兔子洞。)

它们更依赖于彼此相关的值,以便结果符合预期,而且信息量太大。

但是——这是一个重要的但是——圆弧非常强大!

结论

好了,内容很多!但我希望您开始看到路径命令如何有用。我发现它们对于说明数据极其有用。

一旦您知道设置网格、框和曲线等东西有多容易,创建比标准数据可视化库提供的更独特的可视化效果就不需要太多步骤了。

通过本系列文章中学到的一切,您基本上已经完全具备了渲染所有不同类型图表——或其他类型可视化效果的能力。

比如,如何可视化CSS中类似transition-timing-function: ease;的底层三次贝塞尔曲线?这就是我为了弄清楚如何将这些过渡计时函数转换为<animate>标签理解的东西而制作的内容。

SVG有趣且古怪,路径元素可能是您在代码检查中见过的最令人 overwhelming 的符号串。但是,如果您花时间理解底层逻辑,它都会转化为一个美丽简单且极其强大的语法。

我希望通过这对路径解码文章,我成功地揭示了路径绘图的工作原理。如果您想要更多不需要深入研究规范的资源,请尝试MDN关于路径的教程。它简短紧凑,是我学习所有这些内容的主要资源。

然而,自从我写了关于这个主题的深度文章后,我偶然发现了美丽的svg-tutorial.com,它在整体上很好地可视化了SVG编码,但主要特点是我最喜欢的圆弧可视化——Arc Editor。如果您有一个路径想要正确解码,而不必存储这两篇文章中的所有信息,有SVG Path Visualizer,它可以非常好地分解路径信息。

现在:前进,享受在矩阵中玩耍的乐趣。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计