如何使用基本形状简化 SVG 代码

Avatar of Mariana Beldi
Mariana Beldi

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

使用图标的方式有很多,但最佳方案始终包括 SVG,无论它是内联实现还是作为图像文件链接。这是因为它们是在代码中“绘制”的,这使得它们在任何环境中都具有灵活、适应性和可扩展性。

但在使用 SVG 时,它们始终有可能包含大量**不必要的代码**。在某些情况下,内联 SVG 的代码可能很长,导致文档滚动范围变长,操作起来不方便,而且确实比必要的要重一些。

我们可以通过 使用<use>元素重用代码块在一个地方应用原生变量来管理我们的 SVG 样式 来解决这个问题。或者,如果我们在服务器端环境中工作,我们始终可以 添加一些 PHP 代码(或类似代码)来 提取 SVG 文件的内容,而不是直接将其放入其中。

这些方法都很好,但如果我们能够在文件级别解决这个问题,而不是诉诸基于代码的方法,那不是更好吗?我想专注于一个不同的视角:**如何使用基本形状用更少的代码创建相同的图形**。这样,我们就可以在项目中获得更小、可控且语义化的图标,而不会牺牲质量或视觉效果。我将通过不同的示例来探讨常用图标的代码,以及如何使用一些最简单的 SVG 形状来重新绘制它们。

以下是我们即将处理的图标

Showing an close icon in the shape of an x, a clock with the hands pointing at 3 o-clock, and a closed envelope.

让我们看看我们可以使用哪些基本形状来创建这些图标,从而使代码保持简洁。

嘘! 这是我在 holasvg.com 上创建的更长的简单图标列表!阅读完本文后,您将了解如何修改它们并使其成为自己的图标。

使用<line>元素简化关闭图标

这是从 flaticon.com 下载并由 pixel-perfect 创建的“关闭”或“交叉”图标的代码。

在这个例子中,所有操作都发生在<path>内部,数据属性(d)中包含许多命令和参数。这个 SVG 所做的是从其边界追踪形状。

使用 mavo.io 的快速演示

如果您熟悉 Illustrator,这相当于绘制两条独立的线条,将其转换为形状,然后使用路径查找器将两者组合以创建一个复合形状。

<path>元素允许我们绘制复杂的形状,但在这种情况下,我们可以使用两条线创建相同的图形,同时保持相同的外观。

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50" height="50" overflow="visible" stroke="black" stroke-width="10" stroke-linecap="round">
   <line x1="0" y1="0" x2="50" y2="50" />
   <line x1="50" y1="0" x2="0" y2="50" />
</svg>

我们首先定义了一个从 0,0 到 50,50 的viewBox。您可以选择任何您喜欢的尺寸;SVG 将始终很好地缩放至您定义的任何宽度和高度。为了简化操作,在本例中,我还定义了 50 个单位的内联宽度和高度,这避免了绘图中的额外计算。

要使用<line>元素,我们需要声明线条起点的坐标和终点的坐标。在本例中,我们从x=0 y=0开始,到x=50 y=50结束。

Grid of the coordinate system.

以下是代码中的样子

<line x1="0" y1="0" x2="50" y2="50" />

第二条线将从x=50 y=0开始,到x=0 y=50结束。

<line x1="50" y1="0" x2="0" y2="50" />

SVG 描边默认没有颜色——这就是我们在stroke属性上添加black值的原因。我们还为stroke-width属性指定了 10 个单位的宽度,并为stroke-linecap指定了round值,以复制原始设计的圆角。这些属性直接添加到<svg>标签中,因此两条线都将继承它们。

<svg ... stroke="black" stroke-width="10" stroke-linecap="round" ...>

现在描边比其默认大小 1 个单位大了 10 个单位,线条可能会被viewBox裁剪。我们可以将点向viewBox内部移动 10 个单位,或者向样式中添加overflow=visible

等于 0 的值可以删除,因为 0 是默认值。这意味着这两条线最终只有两行非常短的代码。

<line x2="50" y2="50" />
<line x1="50" y2="50" />

仅仅将<path>更改为<line>,我们不仅创建了一个更小的 SVG 文件,而且创建了一个更具语义性和可控性的代码块,这使得未来的任何维护都变得更加容易。视觉效果与原图完全相同。

相同的交叉,不同的代码。

使用<circle><path>元素简化时钟图标

我从 The Noun Project 获取了由 barracuda 创建的时钟图标示例。

此形状也使用<path>绘制,但我们还有很多与所用软件和文件许可证相关的命名空间和 XML 指令,我们可以删除它们而不会影响 SVG。你能说出创建图标时使用了哪个插图编辑器吗?

让我们使用圆形和具有更简单命令的路径从头开始重新创建它。同样,我们需要从viewBox开始,这次是从 0,0 到 100,100,并且宽度和高度与这些单位匹配。

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100" fill="none" stroke="black" stroke-width="10" stroke-linecap="round" stroke-linejoin="round">
  <circle cx="50" cy="50" r="40"/>
  <path d="M50 25V50 H75" /> 
</svg>

我们将之前的图标相同的样式保留在<svg>标签内。fill默认为black,因此我们需要显式地为其指定none值以将其移除。否则,圆形将具有实心黑色填充,遮挡其他形状。

要绘制<circle>,我们需要指示半径所在的中心点。我们可以通过cx(中心 x)和cy(中心 y)来实现。然后r(半径)将声明圆的大小。在本例中,半径略小于viewBox,因此当描边宽度为 10 个单位时不会被裁剪。

所有这些字母是什么意思?请查看 Chris Coyier 的 SVG 语法图解指南 以了解 SVG 语法的入门知识。

我们可以对时针使用<path>,因为它有一些非常有用且简单的绘制命令。在d(数据)内部,我们必须以M(移动到)命令开头,后跟我们将开始绘制的坐标,在本例中为 50,25(靠近圆的顶部中心)。

V(垂直)命令之后,我们只需要一个值,因为我们只能使用负数或正数向上或向下移动。正数将向下移动。H(水平)也是如此,后跟一个正数 75,它将向右绘制。所有命令都使用大写字母,因此我们选择的数字将是网格中的点。如果我们决定使用小写字母(相对命令),则数字将是我们沿一个方向移动的单位数,而不是坐标系中的绝对点。

相同的时钟,不同的代码。

使用<rect><polyline>元素简化信封图标

我在 Illustrator 中绘制了信封图标,没有扩展原始形状。以下是导出后的代码。

Illustrator 提供了一些 SVG 选项来导出图形。我在“CSS 属性”下拉列表中选择了“样式元素”,以便我可以拥有一个包含类别的<style>标签,我可能希望将其移动到 CSS 文件中。当然,在 SVG 中应用样式的方式有很多种。

在此代码中我们已经有了基本形状!我在 Illustrator 中取消选中了“形状到路径”选项,这很有帮助。我们可以使用 SVGOMG 进一步优化它,以删除注释、XML 指令和不必要的数据,例如空元素。如果需要,我们可以从那里手动删除其他额外内容。

我们已经得到了一些更简洁的内容

<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 310 190" xml:space="preserve">
  <style>.st0{fill:none;stroke:#000;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10}
  </style><rect x="5" y="5" class="st0" width="300" height="180"/>
  <polyline class="st0" points="5 5 155 110 305 5"/>
</svg>

我们可以删除更多内容而不会影响信封的视觉外观,包括:

  • version="1.1"(自 SVG 2 以来,此属性已弃用
  • id="Layer_1"(此属性没有意义或用途)
  • x="0"(这是默认值)
  • y="0"(这是默认值)
  • xml:space="preserve"(自 SVG 2 以来,此属性已弃用
<svg xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 310 190">
  <style>.st0{fill:none;stroke:#000;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10}
  </style>
  <rect x="5" y="5" class="st0" width="300" height="180"/>
  <polyline class="st0" points="5 5 155 110 305 5"/>
</svg>

如果我们真的想要更激进地优化,可以将 CSS 样式移动到单独的样式表中。

<rect>需要一个起点,从中我们将扩展宽度和高度,因此让我们使用x="5"y="5"作为左上角点。从那里,我们将创建一个宽度为 300 个单位,高度为 180 个单位的矩形。就像时钟图标一样,我们将使用 5,5 作为起点,因为我们有一个 10 个单位的描边,如果坐标位于 0,0,它将被裁剪。

<polyline> 类似于 <line>,但它可以定义无限多个点,这些点以坐标对的形式依次排列在 points 属性中,其中坐标对中的第一个数字表示 x,第二个数字表示 y。使用逗号分隔序列更容易阅读,但也可以用空格代替逗号,而不会影响结果。

同样的外形,不同的代码。

额外形状!

我没有包含可以使用 <polygon><ellipse> 形状简化的图标示例,但这里有一个快速使用它们的方法。

<polygon><polyline> 相同,只是此元素始终定义一个闭合形状。这是一个来自 MDN 的示例

还记得我们之前为时钟图标绘制的圆圈吗?将 r(半径)替换为 rxry。现在您有两个不同的半径值。这是另一个来自 MDN 的示例

总结

我们在短时间内涵盖了很多内容!虽然我们使用示例演示了优化 SVG 的过程,但我希望您能从这篇文章中获得以下收获

  • 请记住,压缩始于在插图软件中绘制 SVG 的方式。
  • 使用可用的工具,例如 SVOMG,来压缩 SVG。
  • 如有必要,手动删除不必要的元数据。
  • 用基本形状替换复杂的路径。
  • <use> 是“内联”SVG 以及建立您自己的可重用图标库的好方法。

通过组合这些基本形状可以创建多少个图标?

我在 holasvg.com/icons 上整理我的列表,我将不断在这里上传更多图标和功能,现在您知道只需更改几个数字就可以轻松修改它们。来吧,让它们成为你的!