我在这里已经多次谈到了 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 的当前版本匹配。
链接!
- 规范
- SVG 堆栈 由 Simuari 发布,其中指向 Erik Maelström 的演示 和 Bear Travis 的演示。
- Jorge Aznar 的演示
- 使用片段标识符改进 SVG 雪碧图 由 Peter Gasston 发布
- SVG 雪碧图 由 Bear Travis 发布
Chris,我刚刚尝试从 SVG 雪碧图中使用 SVG 片段作为 list-style-image,但似乎不起作用,您认为这样的操作是否可行?
很有意思。我以前从未听说过
view
。在浏览器测试示例中,您在
background-image
+background-position
测试中使用了不同的 SVG,这可能会影响时钟图标的显示。当我在本地和服务器上测试它时,
background-position
技巧按预期工作。SVG 的提供方式可能存在某些问题,导致它无法正确显示。这与图像文件无关;发生了一些其他问题。如果我打开 Chrome DOM 检查器并将 background-position 值编辑为完全相同的值,则它可以按预期工作。因此,那里肯定存在 Chrome 或 WebKit 的错误;如果您想进行更多实验,您可以测试它是否特定于 SVG 图像,是否受 SVG 中的 view 元素或 viewBox 属性的影响,等等。
@Chris
文章写得很好,测试页面也很棒。我真的很希望 WebKit 会改变以支持 CSS 图像上的片段,但很高兴知道所有这些技巧在所有主流浏览器的最新版本中都可以在 HTML 图像上使用。
@Amelia: 我能够在本地和服务器上使用相同的 SVG 使其工作。这是我的测试(略微修改自原始版本):http://brokensquare.com/Code/svg-background-position/
我认为 Chrome 和 SVG 在 Codepen 上的提供方式存在一些问题。
这是一个在 CodePen 上的独立演示,我在其中删除了 Normalize 和其他示例,但它仍然无法工作:http://codepen.io/shshaw/pen/kIEFu
这是我外部服务器上的一个相同测试,但仍然引用 CodePen 上的相同 SVG 文件:http://brokensquare.com/Code/svg-background-position/codepen.html
CodePen 中发生了一些 Chrome 不喜欢的事情。
奇怪。在我关闭 CodePen 示例并重新打开它后,Chrome 开始正确显示图标。也许是 Chrome 的渲染引擎与其他 SVG 技术结合使用时出现的问题?
@Shaw
确实很奇怪。我可以确认使用您的 测试笔 出现了同样的不稳定渲染:有时可以工作,有时则不行。现在我在两个不同的选项卡中打开了页面,一个正确显示了图标,另一个则显示了错误的位置。
在我的
Chrome 38.0.2125.111
和Firefox 33.0.2
中,在OS X 10.10
上都不起作用。这很有趣,但是(除非我错过了什么)这是一个手动过程,不是吗?此外,通过使用此过程,您可以更改图像颜色吗?
谢谢!
@Allesandro
我不确定你所说的“手动过程”是什么意思——你需要创建正确的标记和 URL 并适当地定位你的图形,但你也可以说 PNG 雪碧图也是如此。
当图标嵌入为图像(无论是使用 HTML img 元素还是 CSS 背景/列表图像)时,你无法使用 CSS 更改图标颜色或样式。图像的样式与主页面完全分离。有人讨论未来的 SVG 规范可能有一种方法可以将参数或 CSS 变量传递给图像,但这仍然是相当假设的。
感谢您的测试。鉴于浏览器支持不稳定,以及在使用 img 元素时无法自定义颜色(或其他 SVG 样式)的事实,我短期内不会使用这种 SVG 雪碧图。
Chris,你写道“<use> 表示内联 SVG”。你是因为 IE 不支持外部 SVG 而这样说的吗?
我仍在试验,但我倾向于使用带有外部 SVG 的 <use>,因为它允许使用 CSS 来定义
最多两个自定义颜色(使用 fill/color 或 stroke/color;这需要一个为这种使用准备好的 SVG 源,但并不难);
使用 CSS 自定义属性定义更多自定义颜色;
填充/不填充;
stroke-width/no stroke。
我现在正在使用所有这些内联 SVG,我想知道我们是否应该推动浏览器更好地支持外部 SVG 的相同技术。
我不理解这些 SVG 东西。我为什么要使用这样的东西?我已经按照您在这里向我们展示的所有技术进行了操作,但我一点也不想使用任何技术。字体图标有什么问题,只有抗锯齿问题?如果我要使用 SVG 的任何优势(SVG 特定的 CSS),我需要在我的 HTML 中包含臃肿的代码,或者/以及在我的 Grunt/Gulp 工作流程中包含 50 多个组件来处理所有事情及其回退。我可以看到 SVG 用于一些复杂插图的意义,但对于当前的扁平化设计趋势,到处都是象形图类型的图标,我根本不明白。你是否感到无聊,需要修复一些没有坏掉的东西?
这是一个比较:https://css-tricks.org.cn/icon-fonts-vs-svg/
这两种技术都解决了同一个问题:将大量图像放入单个请求中。只是 SVG 从很多不同的角度来看,做得更好。
酷!有没有理由不……咳咳……在 SVG 中使用背景?对我来说似乎效果很好。
我一直在尝试一种雪碧图方法,其中符号在 defs 中定义,彼此重叠。然后我使用
use
标签并在那里设置坐标。我使用带有百分比值的 CSS 背景位置,并且图标会缩放。它非常不错,并且似乎在任何地方都能正常工作。这是一个演示
http://codepen.io/noahblon/pen/YPKjKr/
还在我的博客上写了一篇文章,介绍了如何以不同的方式进行样式设计。
http://noahblon.com/coloring-svgs-in-css-background-images/
我正在使用 spritecssgenerator.formfcw.com 进行“切换”。
我认为它仍然是最舒适的方式,但 view 方法也听起来很有趣。