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

本文详细解析SVG路径元素中的曲线与弧线命令,包括二次贝塞尔曲线(Q/T命令)、三次贝塞尔曲线(C/S命令)以及弧线命令(A命令),通过代码示例和可视化演示帮助开发者掌握手动编码SVG路径的技巧。

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

在探索如何手动编码矢量的过程中,Myriam Frisano的第二部分路径深入解析将探讨SVG最强大元素中最复杂的方面。她将帮助您理解曲线和弧线构建的基本规则和功能。到最后,您的工具包将准备好处理用代码绘制所需的所有类型的任务——即使有些线条曲折蜿蜒。

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

前期文章简要回顾

如果您是第一次接触本系列,建议您熟悉手动编码SVG的基础知识,以及<marker>的工作原理,并对animate有基本了解,因为本指南不会解释它们。还建议了解<path> d属性中的M/m命令(我写了前述关于路径线命令的文章以提供帮助)。

注意:本文将仅关注曲线和弧线命令的语法,不提供路径元素的介绍。

在开始之前,我想快速回顾一下我如何编码SVG,即使用JavaScript。我不喜欢处理数字和数学,阅读每个属性都填满数字的SVG代码让我失去所有理解。通过给坐标命名,并让所有数学易于解析和全部写出,我对这种类型的代码有了更好的体验,我认为您也会如此。

由于本文的目标是关于理解路径语法,而不是关于进行放置或如何利用循环和其他更基本的东西,我不会带您完成每个示例的整个设置。我会分享一些代码片段,但请注意,它可能从CodePen略有调整或简化,以使文章更易于阅读。然而,如果有关于不在CodePen演示文本中的代码的具体问题——评论部分总是开放的。

为了保持所有框架无关,代码是用原生JavaScript编写的,尽管在实践中,处理复杂图像时强烈推荐TypeScript。

绘制贝塞尔曲线

能够绘制线条、多边形、折线及其复合版本都很有趣和不错,但路径还可以做更多,而不仅仅是提供基本语义SVG标签的更隐秘实现。

其中一种附加类型是贝塞尔曲线。

有多个不同的曲线命令。这就是点和控制点的想法出现的地方。

贝塞尔数学绘图超出了本文的范围。

但是,Freya Holmér有一个视觉上华丽的视频叫做《贝塞尔曲线之美》,它深入探讨了三次和二次贝塞尔曲线的构造,具有美丽的动画,数学变得更容易消化。

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

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

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

这就是我将在以下视觉效果中使用标记和动画的原因。您会注意到我使用的标记是矩形和圆形,并且由于它们连接到线条,我可以利用标记,然后为自己节省大量动画时间,因为这些附加元素被装配到系统中。(并且为单个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轴旋转使得没有解(基本上,椭圆不够大,无法从当前点到达新端点),则椭圆被均匀缩放,直到恰好有一个解(直到椭圆刚好足够大)。

有关此缩放操作的数学公式,请参阅附录部分超出范围半径的校正。”

— 9.5.1 超出范围椭圆弧参数

所以,真的,那种堆叠只是良好和优雅的错误处理,而不是它的本意。因为顶行是弧应该如何使用的。

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

xAxisRotation

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

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

扫描标志

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

大弧标志

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

弧通常需要比我乐意做的更烦人的圆形数字处理(一旦弧度开始发挥作用,我倾向于陷入必须重新学习太多我乐意忘记的数学的兔子洞。)

它们更依赖于值彼此相关,以便结果如预期,并且有太多信息输入。

但是——这是一个很大的但是——弧非常强大!

结论

好吧,那很多!然而,我希望您开始看到路径命令如何有帮助。我发现它们对于说明数据非常有用。

一旦您知道设置诸如网格、框和曲线之类的东西是多么容易,就不需要太多步骤来创建比标准数据可视化库提供的更独特的可视化。

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

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

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

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

然而,自从我写了关于这个主题的深入探讨以来,我偶然发现了美丽的svg-tutorial.com,它很好地可视化了SVG编码作为一个整体,但主要特征是我最喜欢的弧视觉效果在弧编辑器中。如果您有一个路径想要正确解码,而不必存储这两篇文章中的所有信息,有SVG Path Visualizer,它很好地分解了路径信息。

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

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