SVG片段标识符(Fragment Identifiers)如何工作

发布于 彦子

我之前谈到了一点关于SVG<use>的内容——使用它来创建图标系统。<use>的美妙之处在于你可以定义一次SVG,然后在其它地方可以对其多次引用。这种特性使得我们可以创建图标系统,解决“多张图仅需要发一次请求,因为这super高效”这个我们过去用CSS sprite和图标字体解决的问题。

但是<use>意味着内联SVG。当你想要使用一个比较大的SVG文件中的一部分时,如使用SVG作为<img>background-image引入。这就用到片段标识符啦。

准备好SVG

一个方法是像“CSS”sprite一样将SVG的内容排列好(这大概可以叫做SVG sprite)。

准备好SVG

我们这样排的原因是通过移动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。

SVG

设置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网站追踪了片段标识符的支持情况。可惜它不是能否工作这么简单,因为它要看浏览器,可能在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都没问题。

扩展参考


本文根据@CHRIS COYIER的《How SVG Fragment Identifiers Work》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:https://css-tricks.com/svg-fragment-identifiers-work/

彦子

在校学生,本科计算机专业。逗比一枚,热爱前端热爱生活,喜欢CSS喜欢JavaScript喜欢SVG,爱玩PS玩AI玩啊逗比的软件。努力向上,厚积薄发。

如需转载,烦请注明出处:https://www.fedev.cn/svg/svg-fragment-identifiers-work.htmlnike free run 5.0 size