使用图标的方式有很多,但最佳方案始终包括 SVG,无论它是内联实现还是作为图像文件链接。这是因为它们是在代码中“绘制”的,这使得它们在任何环境中都具有灵活、适应性和可扩展性。
但在使用 SVG 时,它们始终有可能包含大量**不必要的代码**。在某些情况下,内联 SVG 的代码可能很长,导致文档滚动范围变长,操作起来不方便,而且确实比必要的要重一些。
我们可以通过 使用<use>
元素重用代码块 或 在一个地方应用原生变量来管理我们的 SVG 样式 来解决这个问题。或者,如果我们在服务器端环境中工作,我们始终可以 添加一些 PHP 代码(或类似代码)来 提取 SVG 文件的内容,而不是直接将其放入其中。
这些方法都很好,但如果我们能够在文件级别解决这个问题,而不是诉诸基于代码的方法,那不是更好吗?我想专注于一个不同的视角:**如何使用基本形状用更少的代码创建相同的图形**。这样,我们就可以在项目中获得更小、可控且语义化的图标,而不会牺牲质量或视觉效果。我将通过不同的示例来探讨常用图标的代码,以及如何使用一些最简单的 SVG 形状来重新绘制它们。
以下是我们即将处理的图标

让我们看看我们可以使用哪些基本形状来创建这些图标,从而使代码保持简洁。
嘘! 这是我在 holasvg.com 上创建的更长的简单图标列表!阅读完本文后,您将了解如何修改它们并使其成为自己的图标。
<line>
元素简化关闭图标
使用这是从 flaticon.com 下载并由 pixel-perfect 创建的“关闭”或“交叉”图标的代码。
在这个例子中,所有操作都发生在<path>
内部,数据属性(d
)中包含许多命令和参数。这个 SVG 所做的是从其边界追踪形状。

如果您熟悉 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
结束。

以下是代码中的样子
<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
(半径)替换为 rx
和 ry
。现在您有两个不同的半径值。这是另一个来自 MDN 的示例
总结
我们在短时间内涵盖了很多内容!虽然我们使用示例演示了优化 SVG 的过程,但我希望您能从这篇文章中获得以下收获
- 请记住,压缩始于在插图软件中绘制 SVG 的方式。
- 使用可用的工具,例如 SVOMG,来压缩 SVG。
- 如有必要,手动删除不必要的元数据。
- 用基本形状替换复杂的路径。
<use
> 是“内联”SVG 以及建立您自己的可重用图标库的好方法。
通过组合这些基本形状可以创建多少个图标?
我在 holasvg.com/icons 上整理我的列表,我将不断在这里上传更多图标和功能,现在您知道只需更改几个数字就可以轻松修改它们。来吧,让它们成为你的!
特别是对于图标,我认为手动编写 SVG 是最佳方法。我发现使用形状效率低下,因为它冗长。并且在路径代码中保存的内容在 CSS 中丢失了。
我喜欢用单个路径手动编写我的 SVG 图标。当您充分利用“关闭”指令和绘制方向时,它可能是一个令人愉快的谜题。
我将路径本身存储在 JavaScript 数组中,并在运行时创建 SVG 元素以避免混乱。
太棒了!最终,关键在于了解代码的作用。
那是真的。
好文章。我想我会尝试剪掉一些大型 SVG 并使用更少的代码。
加油!谢谢!!
我喜欢你所做的。你有没有想过添加状态图标
错误、警告、确定、信息
外部形状,正方形、三角形(可能带圆角)和圆形与您的技术完全一致。您绘制的 X 可以很好地用于错误,据我所知,您可以在 svg 中插入字符,因此“!”用于警告,并且“i”应该可用。我不确定检查标记,它可能需要是一个路径,尽管它可以是几条线。
我喜欢这个主意,我可能会很快将它们添加到 https://holasvg.com/icons 中,谢谢!
在网站上,您还可以找到用折线绘制的对勾图标 :)
很棒的教程!
为什么 Flaticon 的“交叉”符号没有像您一样使用明显简洁的代码?当然,它们的图标是为了在网络上使用。
顺便问一下,如何在 SVG 中使用 use?教程中没有提到。
谢谢。
谢谢!
也许并非所有图标都最终用于网络,大多数图标是由没有考虑软件生成的代码的设计师上传的,我想这也没问题!
您可以直接将 SVG 复制粘贴到 HTML 上,它会立即在您的页面上呈现。我不确定这是否回答了你的问题:) 请告诉我!
嗨,Mariana,
您可以用一条线绘制信封(以及关闭图标)
<polyline class="st0" points="5 5 155 110 305 5 5 5 5 185 305 185 305 5" />
<path class="st0" d="M5 5 155 110 305 5 5 5 5 185 305 185 305 5" />
请注意第二个示例中隐含的
M
移动命令。我的建议是,如果需要单独动画/样式化形状,则使用合适的形状,否则使用
<path>
获取较短的版本。仅当您将 SVG 作为外部文件引用或想要在 Illustrator 中打开它时(至少在我的 CS6 版本中),才需要
xmlns
属性。是的,当然!有很多方法可以获得相同的图标。
我只是想介绍一个不同的基本形状(矩形)。
好文章!谢谢,Mariana。