我之前谈到了一点关于SVG<use>
的内容——使用它来创建图标系统。<use>
的美妙之处在于你可以定义一次SVG,然后在其它地方可以对其多次引用。这种特性使得我们可以创建图标系统,解决“多张图仅需要发一次请求,因为这super高效”这个我们过去用CSS sprite和图标字体解决的问题。
但是<use>
意味着内联SVG。当你想要使用一个比较大的SVG文件中的一部分时,如使用SVG作为<img>
或background-image
引入。这就用到片段标识符啦。
准备好SVG
一个方法是像“CSS”sprite一样将SVG的内容排列好(这大概可以叫做SVG sprite)。
我们这样排的原因是通过移动viewBox
,然后让这张图的部分内容显示出来就够了,和CSS sprite一样。
在这个小demo中,我们使用的三个图标都是32X32
的大小。所以这个文件是32X96
。我们可以这样子玩耍viewBox
。
- 显示整个文件的
viewBox
参数:0,0,32,96
; - 显示第一个icon的
viewBox
参数:0,0,32,32
; - 显示第二个icon的
viewBox
参数:0,32,32,32
; - 显示第三个icon的
viewBox
参数:0,64,32,32
;
这四个参数分别代表:left
,top
,width
,height
。注意第二个参数top
,每次增加32
。这样就可以显示出一个图片的不同部分啦。
把这些viewBox
参数添加到SVG文件中
你可以把这些特定的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>
元素可以像上面这样独立开来,也可以包裹其它的元素,然后里面的viewBox
就会显示id指向的内容,如下:
<!-- 这个viewBox会显示片段标识符为`match-me`的内容 -->
<view id="match-me" viewBox="0 64 32 32">
<rect ...>
</view>
规范中的demo。
针对HTML的语法
给通过<img>
引入的SVG应用这个特殊的viewBox
值,你可以这样写:
<!-- 第一个icon -->
<img src="sprite.svg#svgView(viewBox(0, 0, 32, 32))" alt="">
或者,如果你已经设置好了<view>
元素,你可以这样通过名称引用:
<!-- 第二个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 sprite技术,吧SVG文件当成背景图像,然后移动背景图像的位置:
.icon-heart {
background: url("sprite.svg") no-repeat;
background-size: 32px 96px;
background-position: 0 -32px;
}
我只是想把图标一个一个堆叠成栈
如果图标的viewBox
都是相同的,你基本上就只需要设置在需要的时候hide/show
,这也非常容易。
在同一个空间中设计它们(或使用一个构建工具来完成,都可以),保证图标都是相同的尺寸。这里,我给每个图标都设置了一个单独的id。
设置hide/show
的技巧,可以在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网站追踪了片段标识符的[支持情况]((http://caniuse.com/#feat=svg-fragment)。可惜它不是能否工作这么简单,因为它要看浏览器,可能在HTML中可以,但是CSS不可以,或者还有更怪异的情况。
这是我的测试页面:
我没有做过非常详细的浏览器支持情况的整理,但是这里有个简略的版本:
- Firefox中工作没有问题。
- IE 11也没问题。IE 9和IE 10在
background-position
有点怪怪的,但其它的都没问题。 - 当前版本的Chrome/Safari/Opera(38/8/25)处理HTML的
<img>
都没问题,但是CSS有问题,包括background-position
。原Blink内核的Opera也是一样,非常有趣。 - iOS8.1唯一可以处理的只有引用
<view>
的<img>
。 - Android 4.4可以处理的只有
background-position
(这个是完全没有真正使用片段标识符的)。Android 5匹配当前的Chrome/Safari/Opera都没问题。
扩展参考
- Spec
- SVG Stacks by Simuari, which points to a demo by Erik Maelström and a demo by Bear Travis.
- Demo by Jorge Aznar
- Better SVG Sprites With Fragment Identifiers by Peter Gasston
- SVG Sprite Sheets by Bear Travis
本文根据@CHRIS COYIER的《How SVG Fragment Identifiers Work》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:https://css-tricks.com/svg-fragment-identifiers-work/。
如需转载,烦请注明出处:http://www.w3cplus.com/svg/svg-fragment-identifiers-work.html