SVG 片段标识符的工作原理

Avatar of Chris Coyier
Chris Coyier 发布

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

我在这里已经多次谈到了 SVG 的 <use> 以及如何使用它来构建 图标系统<use> 的优点在于,您可以引用在其他地方定义的某个 SVG 的一部分,并在其他地方绘制该部分。此功能允许您构建一个完整的系统,从而解决我们过去使用 CSS 雪碧图和图标字体解决的“在一个请求中加载多个图像,因为这样效率更高”的问题。

但是,<use> 表示内联 SVG。当您想在 SVG 作为 <img> 或 SVG 作为 background-image 时使用更大 SVG 的一部分时,它无济于事。这就是片段标识符发挥作用的地方。

准备 SVG

一种方法是像图形“CSS”雪碧图一样布局 SVG(雪碧图,我想,就称它为雪碧图)。

我们之所以以这种方式进行操作,是因为我们最终将调整一些 viewBox 数字,以仅显示此图像的一部分,就像我们过去使用 CSS 雪碧图一样。

在这个小演示中,我们使用了三个 32×32 的图标。因此文档为 32×96。我们可以这样考虑 viewBox 的操作

整个文档的 viewBox 0 0 32 96
仅显示顶部图标的 viewBox 0 0 32 32
仅显示中间图标的 viewBox 0 32 32 32
仅显示底部图标的 viewBox 0 64 32 32

viewBox 属性的格式为:左、上、宽、高。请注意第二个属性(顶部),每次递增 32。我们只是显示了整个文档的不同部分。

在 SVG 本身中添加这些特殊的 viewBox

您可以将这些特殊的、特定的 viewBox 值放入 SVG 中的 <view> 元素中,该元素专门用于此目的。

<view id="icon-clock-view" viewBox="0 0 32 32" />
<view id="icon-heart-view" viewBox="0 32 32 32" />
<view id="icon-arrow-right-view" viewBox="0 64 32 32" />

现在我们将能够从其他地方引用和使用这些值。

<view> 元素可以像这样独立存在,或者它们实际上可以包装其他元素,在这种情况下,当 ID 匹配时,该 viewBox 会生效,因此

<!-- this viewBox takes over if current fragment identifer is #match-me -->
<view id="match-me" viewBox="0 64 32 32">
  <rect ...>
</view>

规范中此类内容的演示

HTML 语法

要将这些特殊的 viewBox 值应用于 SVG 作为 <img>,您可以这样做

<!-- top icon -->
<img src="sprite.svg#svgView(viewBox(0, 0, 32, 32))" alt="">

或者,如果您已设置 <view> 元素,则只需按名称引用它们即可。

<!-- middle icon -->
<img src="sprite.svg#icon-heart-view" alt="">

CSS 语法

您可以在 CSS 中图像路径中声明一个特殊的 viewBox

.icon-clock {
  background: url("sprite.svg#svgView(viewBox(0, 0, 32, 32))") no-repeat;
}

或者,如果您已设置 <view> 元素,则可以引用它。

.icon-clock {
  background: url(sprite.svg#icon-clock-view) no-repeat;
}

尽管……如果您以这种方式通过 CSS 使用 SVG,并且费尽心思地将 SVG 间隔开来,您可能只想使用 CSS 雪碧图技术并移动背景。

.icon-heart {
  background: url("sprite.svg") no-repeat;
  background-size: 32px 96px;
  background-position: 0 -32px;
}

我只是想将图标堆叠在一起。

如果所有图标都具有相同的 viewBox,并且您实际上只想根据需要隐藏/显示它们,那么这会变得容易一些。

将它们全部设计在同一空间中(或使用执行此操作的构建工具或任何其他工具)。在这里,我将每个图标都放在具有唯一 ID 的自己的组中。

使隐藏/显示工作生效的技巧是嵌入一些 CSS,这些 CSS 1) 隐藏所有内容 2) 显示与片段标识符匹配的图标。CSS 可以胜任这项工作,因为有 :target 选择器

SVG 中的所有内容

<defs>
  <style>
    g {
      display: none;
    }
    g:target {
      display: inline;
    }
  </style>
</defs>

<g id="icon-clock">
  <path d="M20.6,23.3L14,16.7V7.9h4v7.2l5.4,5.4L20.6,23.3z M16-0.1c-8.8,0-16,7.2-16,16s7.2,16,16,16s16-7.2,16-16S24.8-0.1,16-0.1z
		 M16,27.9c-6.6,0-12-5.4-12-12s5.4-12,12-12s12,5.4,12,12S22.6,27.9,16,27.9z"/>
</g>
<g id="icon-heart">
  <path d="M32,11.2c0,2.7-1.2,5.1-3,6.8l0,0L19,28c-1,1-2,2-3,2s-2-1-3-2L3,18c-1.9-1.7-3-4.1-3-6.8C0,6.1,4.1,2,9.2,2
		c2.7,0,5.1,1.2,6.8,3c1.7-1.9,4.1-3,6.8-3C27.9,1.9,32,6.1,32,11.2z"/>
</g>
<g id="icon-arrow-right">
  <path d="M32,15.9l-16-16v10H0v12h16v10L32,15.9z"/>
</g>

浏览器支持

Can I Use 跟踪片段标识符的支持情况。不幸的是,这并不像是否有效那样简单,因为根据浏览器的不同,它可能在 HTML 中有效而在 CSS 中无效,或者存在一些怪癖。

这是我的测试页面

查看 Chris Coyier 在 CodePen 上创建的 Pen HTML 和 CSS 中的 SVG 片段标识符 (@chriscoyier)。

我还没有为此创建非常详细的浏览器支持说明,但以下是重点

  • Firefox 的所有功能都正常。
  • IE 11 的所有功能都正常。IE 9 和 10 在 background-position 方面有点奇怪(压缩),但在其他方面都正常。
  • Chrome/Safari/Opera 的当前版本(38/8/25)可以很好地处理所有 HTML <img> 技术,但不能处理任何 CSS 技术,包括 background-position 技术。有趣的是,Blink 之前的 Opera 也是如此。 更新:如果引用的 <svg> 上具有正确的宽度和高度属性,则所有方法都能按预期工作。至少现在是如此,从 Chrome (56) / Opera 开始。
  • iOS 8.1 唯一可以处理的是 <img> 引用 <view>
  • Android 4.4 唯一可以正确处理的是 background-position(根本没有使用片段标识符的那个)。Android 5 与上述 Chrome/Safari/Opera 的当前版本匹配。

链接!