首页 >web前端 >css教程 >优化SVG图案的最小尺寸

优化SVG图案的最小尺寸

尊渡假赌尊渡假赌尊渡假赌
尊渡假赌尊渡假赌尊渡假赌原创
2025-03-14 10:18:11648浏览

Optimizing SVG Patterns to Their Smallest Size

最近,我创建了一个砖墙图案,作为我的 #PetitePatterns 系列的一部分,这是一个挑战,我需要在 560 字节(或大约两个推文的大小)内用 SVG 创建看起来很自然的图案或纹理。为了满足这个限制,我经历了一段旅程,这段旅程教会了我一些优化 SVG 图案的激进方法,以便它们包含尽可能少的代码,而不会影响整体图像质量。

我想带你了解这个过程,并向你展示如何将一个最初为 197 字节的 SVG 图案缩小到只有 44 字节——惊人的 77.7% 的减少!

SVG 图案

这被称为“连续粘结”砖图案。这是最常见的砖图案,你肯定以前见过:每一排砖块都偏移半个砖块的长度,形成重复的交错图案。这种排列非常简单,使 SVG 的 <pattern></pattern> 元素非常适合在代码中重现它。

SVG 的 <pattern></pattern> 元素使用预定义的图形对象,该对象可以在水平和垂直轴上以固定的间隔重复(或“平铺”)。本质上,我们定义一个矩形平铺图案,它会重复以绘制填充区域。

首先,让我们设置砖块的尺寸以及每个砖块之间的间隙。为简单起见,让我们使用清晰的整数:砖块的宽度为 100,高度为 30,它们之间的水平和垂直间隙为 10。

接下来,我们必须确定我们的“基本”图块。“图块”指的是图案图块,而不是物理图块,不要与砖块混淆。让我们使用上面图像中突出显示的部分作为我们的图案图块:第一行中有两个完整的砖块,第二行中有一个完整的砖块夹在两个半个砖块之间。注意间隙的位置和方式,因为这些需要包含在重复的图案图块中。

使用 <pattern></pattern> 时,我们必须定义图案的宽度和高度,它们对应于基本图块的宽度和高度。要获得尺寸,我们需要进行一些计算:

<code>图块宽度 = 2(砖块宽度)  2(间隙)= 2(100)  2(10)= 220
图块高度 = 2(砖块高度)  2(间隙)= 2(30)  2(10)= 80</code>

好的,所以我们的图案图块是 220✕80。我们还必须设置 patternUnits 属性,其中 userSpaceOnUse 值基本上表示像素。最后,添加图案的 id 是必要的,以便在用它绘制另一个元素时可以引用它。

<code><pattern height="80" patternunits="userSpaceOnUse" width="220"></pattern></code>

现在我们已经确定了图块的尺寸,挑战是如何以尽可能少的字节数创建图块的代码,从而呈现图形。这就是我们最终希望达到的目标:

初始标记(197 字节)

在我看来,重现此图案最简单、最明确的方法是绘制五个矩形。默认情况下,SVG 元素的填充为黑色,笔触为透明。这对于优化 SVG 图案非常有效,因为我们不必在代码中明确声明它们。

下面代码中的每一行都定义了一个矩形。宽度和高度始终设置,并且仅当矩形从 0 位置偏移时才设置 x 和 y 位置。

<code><rect height="30" width="100"></rect><rect height="30" width="100" x="110"></rect><rect height="30" width="45" y="40"></rect><rect height="30" width="100" x="55" y="40"></rect><rect height="30" width="55" x="165" y="40"></rect></code>

图块顶行包含两个全宽砖块,第二个砖块定位到 x="110",在砖块之前留有 10 像素的间隙。同样,之后也有 10 像素的间隙,因为砖块在水平轴上的结束位置为 210 像素(110 100 = 210),即使 <pattern></pattern> 宽度为 220 像素。我们需要一点点额外的空间;否则,第二个砖块将与相邻图块中的第一个砖块合并。

第二行(底部)中的砖块是偏移的,因此该行包含两个半个砖块和一个完整的砖块。在这种情况下,我们希望半个砖块合并,因此开头或结尾没有间隙,允许它们与相邻图案图块中的砖块无缝融合。偏移这些砖块时,我们还必须包含半个间隙,因此 x 值分别为 55 和 165。

元素重用(-43B,共 154B)

如此明确地定义每个砖块似乎效率低下。有没有什么方法可以通过重用形状来优化 SVG 图案?

我认为 SVG 的 <use></use> 元素并不广为人知。您可以使用它引用另一个元素,并在使用 <use></use> 的任何位置呈现该引用的元素。这可以节省相当多的字节,因为我们可以省略指定每个砖块的宽度和高度,除了第一个砖块。

也就是说,<use></use> 确实需要付出一些代价。也就是说,我们必须为我们想要重用的元素添加一个 id。

<code><rect height="30" width="100"></rect><use href="#b" x="110"></use><use href="#b" x="-55" y="40"></use><use href="#b" x="55" y="40"></use><use href="#b" x="165" y="40"></use></code>

可能的最小 id 是一个字符,所以我为砖块选择了“b”。<use></use> 元素可以像 <rect></rect> 一样定位,x 和 y 属性作为偏移量。由于我们切换到 <use></use> 后每个砖块现在都是全宽(记住,我们在图案图块的第二行中明确地将砖块减半),我们必须在第二行中使用负 x 值,然后确保最后一个砖块从图块溢出,以便砖块之间无缝连接。这些是可以的,因为图案图块之外的任何内容都会自动被剪掉。

你能发现一些可以更有效地编写的重复字符串吗?接下来让我们研究一下这些。

重写为路径(-54B,共 100B)

<path></path> 可能是 SVG 中最强大的元素。您可以使用其 d 属性中的“命令”绘制几乎任何形状。共有 20 个可用命令,但我们只需要最简单的矩形命令。

这就是我最终得到的结果:

<code><path d="M0 0h100v30h-100z
         M110 0h100v30h-100
         M0 40h45v30h-45z
         M55 40h100v30h-100z
         M165 40h55v30h-55z"></path></code>

我知道,超级奇怪的数字和字母!它们当然都有意义。以下是此特定情况下的情况:

  • M{x} {y}: 基于坐标移动到一个点。
  • z: 关闭当前段。
  • h{x}: 从当前点绘制一条水平线,长度为 x,方向由 x 的符号定义。小写 x 表示相对坐标。
  • v{y}: 从当前点绘制一条垂直线,长度为 y,方向由 y 的符号定义。小写 y 表示相对坐标。

此标记比之前的标记简洁得多(换行符和缩进空格仅用于可读性)。而且,嘿,我们已经设法减少了一半的初始大小,达到 100 字节。尽管如此,有些事情让我觉得这可以更小……

图块修订(-38B,共 62B)

我们的图案图块没有重复的部分吗?很明显,第一行重复了一个完整的砖块,但第二行呢?有点难以看出,但是如果我们将中间的砖块切成两半,就会很明显。

好吧,中间的砖块并没有完全切成两半。存在轻微偏移,因为我们还必须考虑间隙。无论如何,我们刚刚找到了一个更简单的基本图块图案,这意味着更少的字节!这也意味着我们必须将 <pattern></pattern> 元素的宽度从 220 减半到 110。

<code><pattern height="80" patternunits="userSpaceOnUse" width="110"></pattern></code>

现在让我们看看如何使用 <path></path> 绘制简化的图块:

<code><path d="M0 0h100v30h-100z
         M0 40h45v30h-45z
         M55 40h55v30h-55z"></path></code>

大小减少到 62 字节,这已经不到原始大小的三分之一了!但是,为什么在这里停下来,当我们还可以做更多的事情时呢!

缩短路径命令(-9B,共 53B)

值得更深入地研究 <path></path> 元素,因为它提供了更多优化 SVG 图案的提示。在使用 <path></path> 时,我有一个误解,关于填充属性是如何工作的。在我的童年时代,我玩过很多 MS Paint,我了解到任何我想用纯色填充的形状都必须是封闭的,即没有开放点。否则,油漆会从形状中泄漏并溢出到所有内容。

但是,在 SVG 中,情况并非如此。让我引用规范本身:

填充操作通过执行填充操作来填充开放子路径,就好像向路径添加了额外的“closepath”命令以将子路径的最后一点与子路径的第一点连接起来一样。

这意味着我们可以省略闭合路径命令(z),因为填充时会自动认为子路径已关闭。

关于路径命令的另一个有用之处是它们有大小写变体。小写字母表示使用相对坐标;大写字母表示使用绝对坐标。

对于 H 和 V 命令来说,情况有点棘手,因为它们只包含一个坐标。以下是我的描述:

  • H{x}: 从当前点绘制一条水平线到坐标 x。
  • V{y}: 从当前点绘制一条垂直线到坐标 y。

当我们绘制图案图块中的第一个砖块时,我们从 (0,0) 坐标开始。然后我们绘制一条水平线到 (100,0) 和一条垂直线到 (100,30),最后,绘制一条水平线到 (0,30)。我们在最后一行使用了 h-100 命令,但它等效于 H0,是五个字节而不是两个字节。我们可以替换两个类似的出现,并将 <path></path> 的代码减少到:

<code><path d="M0 0h100v30H0
         M0 40h45v30H0
         M55 40h55v30H55"></path></code>

又减少了 9 个字节——我们还能减少多少?

桥接(-5B,共 48B)

阻碍我们完全优化 SVG 图案的最长命令是“移动到”命令,它们分别占用 4、5 和 6 个字节。我们有一个限制是:

路径数据段(如果存在)必须以“moveto”命令开头。

但这没关系。第一个是最短的。如果我们交换行,我们可以得到一个路径定义,其中我们只需要在砖块之间水平或垂直移动。如果我们可以在那里使用 h 和 v 命令而不是 M 会怎么样?

上图显示了如何使用单个路径绘制三个形状。请注意,我们正在利用填充操作会自动关闭 (110,0) 和 (0,0) 之间开放部分的事实。通过这种重新排列,我们还将间隙移动到第二行全宽砖块的左侧。以下是代码的外观,仍然分成每行一个砖块:

<code><path d="M0 0v30h50V0
         h10v30h50
         v10H10v30h100V0"></path></code>

当然,我们现在已经找到了绝对最小的解决方案,因为我们已经减少到 48 字节,对吧?!好吧……

数字修剪(-4B,共 44B)

如果您对尺寸有点灵活,那么我们还可以优化 SVG 图案。我们一直在使用 100 像素的砖块宽度,但这需要三个字节。将其更改为 90 表示每当我们需要编写它时,都会减少一个字节。同样,我们使用了 10 像素的间隙——但是如果我们将其更改为 8,则每次出现都会节省一个字节。

<code><path d="M0 0v30h45V0
         h8v30h45
         v8H8v30h90V0"></path></code>

当然,这也意味着我们必须相应地调整图案尺寸。以下是最终优化的 SVG 图案代码:

<code><pattern height="76" patternunits="userSpaceOnUse" width="98"><path d="M0 0v30h45V0h8v30h45v8H8v30h90V0"></path></pattern></code>

上面代码片段中的第二行(不包括缩进)是 44 字节。我们通过六次迭代从 197 字节达到了这里。这是一个巨大的 77.7% 的尺寸缩减

不过,我想知道……这真的是最小的尺寸吗?我们是否已经查看了所有可能的优化 SVG 图案的方法?

我邀请您尝试进一步缩小此代码,甚至尝试使用替代方法来优化 SVG 图案。我很想看看我们能否利用大众的智慧找到真正的全局最小值!

关于创建和优化 SVG 图案的更多信息

如果您有兴趣了解有关创建和优化 SVG 图案的更多信息,请阅读我关于使用 SVG 滤镜创建图案的文章。或者,如果您想查看 60 多个图案的画廊,您可以查看 PetitePatterns CodePen 集合。最后,欢迎您观看我在 YouTube 上的教程,以帮助您更深入地了解 SVG 图案。

以上是优化SVG图案的最小尺寸的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn