最近,我创建了一个砖墙图案,作为我的 #PetitePatterns 系列的一部分,这是一个挑战,我需要在 560 字节(或大约两个推文的大小)内用 SVG 创建看起来很自然的图案或纹理。为了满足这个限制,我经历了一段旅程,这段旅程教会了我一些优化 SVG 图案的激进方法,以便它们包含尽可能少的代码,而不会影响整体图像质量。
我想带你了解这个过程,并向你展示如何将一个最初为 197 字节的 SVG 图案缩小到只有 44 字节——惊人的 77.7% 的减少!
这被称为“连续粘结”砖图案。这是最常见的砖图案,你肯定以前见过:每一排砖块都偏移半个砖块的长度,形成重复的交错图案。这种排列非常简单,使 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>
现在我们已经确定了图块的尺寸,挑战是如何以尽可能少的字节数创建图块的代码,从而呈现图形。这就是我们最终希望达到的目标:
在我看来,重现此图案最简单、最明确的方法是绘制五个矩形。默认情况下,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。
如此明确地定义每个砖块似乎效率低下。有没有什么方法可以通过重用形状来优化 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 值,然后确保最后一个砖块从图块溢出,以便砖块之间无缝连接。这些是可以的,因为图案图块之外的任何内容都会自动被剪掉。
你能发现一些可以更有效地编写的重复字符串吗?接下来让我们研究一下这些。
<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>
我知道,超级奇怪的数字和字母!它们当然都有意义。以下是此特定情况下的情况:
此标记比之前的标记简洁得多(换行符和缩进空格仅用于可读性)。而且,嘿,我们已经设法减少了一半的初始大小,达到 100 字节。尽管如此,有些事情让我觉得这可以更小……
我们的图案图块没有重复的部分吗?很明显,第一行重复了一个完整的砖块,但第二行呢?有点难以看出,但是如果我们将中间的砖块切成两半,就会很明显。
好吧,中间的砖块并没有完全切成两半。存在轻微偏移,因为我们还必须考虑间隙。无论如何,我们刚刚找到了一个更简单的基本图块图案,这意味着更少的字节!这也意味着我们必须将 <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 字节,这已经不到原始大小的三分之一了!但是,为什么在这里停下来,当我们还可以做更多的事情时呢!
值得更深入地研究 <path></path>
元素,因为它提供了更多优化 SVG 图案的提示。在使用 <path></path>
时,我有一个误解,关于填充属性是如何工作的。在我的童年时代,我玩过很多 MS Paint,我了解到任何我想用纯色填充的形状都必须是封闭的,即没有开放点。否则,油漆会从形状中泄漏并溢出到所有内容。
但是,在 SVG 中,情况并非如此。让我引用规范本身:
填充操作通过执行填充操作来填充开放子路径,就好像向路径添加了额外的“closepath”命令以将子路径的最后一点与子路径的第一点连接起来一样。
这意味着我们可以省略闭合路径命令(z),因为填充时会自动认为子路径已关闭。
关于路径命令的另一个有用之处是它们有大小写变体。小写字母表示使用相对坐标;大写字母表示使用绝对坐标。
对于 H 和 V 命令来说,情况有点棘手,因为它们只包含一个坐标。以下是我的描述:
当我们绘制图案图块中的第一个砖块时,我们从 (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 个字节——我们还能减少多少?
阻碍我们完全优化 SVG 图案的最长命令是“移动到”命令,它们分别占用 4、5 和 6 个字节。我们有一个限制是:
路径数据段(如果存在)必须以“moveto”命令开头。
但这没关系。第一个是最短的。如果我们交换行,我们可以得到一个路径定义,其中我们只需要在砖块之间水平或垂直移动。如果我们可以在那里使用 h 和 v 命令而不是 M 会怎么样?
上图显示了如何使用单个路径绘制三个形状。请注意,我们正在利用填充操作会自动关闭 (110,0) 和 (0,0) 之间开放部分的事实。通过这种重新排列,我们还将间隙移动到第二行全宽砖块的左侧。以下是代码的外观,仍然分成每行一个砖块:
<code><path d="M0 0v30h50V0 h10v30h50 v10H10v30h100V0"></path></code>
当然,我们现在已经找到了绝对最小的解决方案,因为我们已经减少到 48 字节,对吧?!好吧……
如果您对尺寸有点灵活,那么我们还可以优化 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 滤镜创建图案的文章。或者,如果您想查看 60 多个图案的画廊,您可以查看 PetitePatterns CodePen 集合。最后,欢迎您观看我在 YouTube 上的教程,以帮助您更深入地了解 SVG 图案。
以上是优化SVG图案的最小尺寸的详细内容。更多信息请关注PHP中文网其他相关文章!