未来必热:SVG Sprite技术介绍

一、Sprite技术

这里所说的Sprite技术,没错,类似于CSS中的Sprite技术。图标图形整合在一起,实际呈现的时候准确显示特定图标。

另,本文图片甚多,爪机党继续浏览需慎重。

二、SVG Sprite与symbol元素

目前,SVG Sprite最佳实践是使用symbol元素。symbol元素是什么呢?单纯翻译的话,是“符号”的意思。然,这个释义并不符合这里的场景。不知大家有没有用过Flash,symbol实际上就类似于Flash中的“影片剪辑”、或者“元件”。

因此,我个人觉得,symbol应该解释为“元件”最为恰当!

那,symbol和SVG Sprite又有什么关系呢?

我们可以把SVG元素看成一个舞台,而symbol则是舞台上一个一个组装好的元件,这这些一个一个的元件就是我们即将使用的一个一个SVG图标。

于是,对于一个集合了三个SVG图标的SVG元素的代码结构会是这样:

<svg>
    <symbol>
        <!-- 第1个图标路径形状之类代码 -->
    </symbol>
    <symbol>
        <!-- 第2个图标路径形状之类代码 -->
    </symbol>
    <symbol>
        <!-- 第3个图标路径形状之类代码 -->
    </symbol>
</svg>

每一个symbol就是一个图标元件,但是,只有上面的代码,是无法呈现类似下面的效果的:

为何?

因为,舞台上只是放置了图标,如果你不使用(use),是看不见的。就好比你女朋友买了几箱的衣服放家里,如果不穿出去,谁知道她这么土豪呢?

因此,还差一个“使用”,也就是SVG中的元素。

三、SVG中的use元素

use元素是SVG中非常强大,非常重要的一个元素,尤其在Web开发中,为何?

两点:

  1. 可重复调用;
  2. 跨SVG调用;

  3. 可重复调用

你好不容易,用了几十个坐标值,好不容易绘制了一个图形,如果你想再弄一个同样造型,但位置不同的图形出来,你会怎么办?——再复制一遍代码?别说笑了,(如果真那样)SVG文件的尺寸赶得上二师兄的腰围了。

使用元素就可以,看下面的板栗:

<svg>
  <defs>
    <g id="shape">
        <rect x="0" y="0" width="50" height="50" />
        <circle cx="0" cy="0" r="50" />
    </g>
  </defs>

  <use xlink:href="#shape" x="50" y="50" />
  <use xlink:href="#shape" x="200" y="50" />
</svg>

结果是(IE9+浏览器可见):

首先,注意到没有,use元素是通过xlink:href属性,寻找要使用的元素的。#shape对应的就是id为shape的元素。use元素可以有自己的坐标,以及支持transform变换,甚至可以use其他use元素。

这里,两个use元素使用的是同一个g元素(组合),从而实现了图形的重复调用功能。

  1. 跨SVG调用

SVG中的use元素可以调用其他SVG文件的元素,只要在一个文档中。

紧接着上面的板栗:

<svg width="500" height="110"><use xlink:href="#shape" x="50" y="50" /></svg>

结果仍是那个图形:

而这个跨SVG调用就是“SVG Sprite技术”的核心所在。

试想下,我们只要在页面某处载入一个充满Sprite(symbol)的SVG文件(或直接include SVG代码),于是,在页面的任何角落,只要想使用这个图标,只要简单这一点代码就可以了:

<svg class="size"><use xlink:href="#target" /></svg>

图标尺寸CSS控制,里面只有一个仅有xlink:href属性的use元素,Done! 完成!

也即是说,在HTML层面,图标使用的代码成本,跟传统的CSS Sprite或者流行的font-face几乎无异,代码简洁,而且很好维护。所有的SVG图标都在一个SVG源上。retina良好,尺寸可任意拉伸,且颜色可控,真乃Web图标的未来之星。

吹的嘴巴都干了,上个简单的demo给大家瞅瞅, 我先去给水排水~~

您可以狠狠地点击这里:SVG Sprite使用示意demo

代码如下截图:

效果为:

总结下就是:symbol + use => SVG Sprite

诸位会不会觉得本文的内容快要结束了呢?

嘻嘻嘻嘻,顶多才1/3,下面的才是重点,打起精神,走起~~

四、SVG Sprite实际应用的阻碍

两大问题:

  1. SVG图标从何而来?
  2. SVG图标如何变成symbol并整合在一起?

第1个问题,SVG图标从何而来?

“远在天边近在眼前”,CSS3 font-face的逐渐升温,让很多font-face工具诞生了。例如以前我介绍过的国外的icomoon.io,或者国内阿里系的iconfont.cn, 或者bootsrap 3粉们的font-awesome

这些图标实际上都是使用SVG作为媒介的,所以,根本就不要担心“SVG图标从何而来”,这图标多的就像牛魔王身上的虱子——一抖一大把啊!

OK,如何获得呢?直接下载。拿iconfont.cn示意,点击某一图标的下载按钮:

点击下载SVG:

于是,我们就得到了SVG图标啦!如果你还需要其他图标,也按照这个步骤一个一个下载下来,很简单吧~ 然后把它们放在一个文件夹中,以备后用。

下面最关键的是第2个问题,如何合并这些SVG到一个SVG上?同时满足使用的是symbol标签,并可以使用裸露的use元素(除了xlink:href没有其他属性)调用呢?

很长很funny, 要专门独立两段,

五、关于合并:偏开发的前端看这里

这里的方法适用于偏开发的前端,一些对指令无感的妹子们,可以看下面偏设计的方法。

Github上,有个名为svgstore的grunt插件,地址戳这里。如果您还不了解grunt, 可以看看这两篇我觉得不错的文章:“使用GruntJS构建Web程序 (1)”和“使用GruntJS构建Web程序 (2)”.

grunt改装的装好后,执行下面的指令,

npm install grunt-svgstore --save-dev

烧个香保佑安装成功,然后你就会看到在node_modules文件夹中一个名为grunt-svgstore的子文件夹(如下):

这个名为grunt-svgstore的文件夹中有个名为Gruntfile.js的文件,据我个人理解,这个JS是所有grunt插件要使用的配置文件。

我在里面做了一件事情,加了这么一行代码:

为什么要加这么行代码呢?因为grunt-svgstore项目README.md让加的,所以我就加了。

你会看到

svgstore: {

}

中有一大堆的配置什么的,这些都是用来独立测试各个API的(options对象中各个键就是API名称),让你知道此插件各个API都是干嘛用的,具体释义参见该Github项目。忍不住提一下,如果你希望SVG图标颜色可以在CSS中通过fill控制,cleanup设置为true.

OK, 准备工作完毕,下面来实践下。我把从iconfont.cn上下载下来的3个SVG都放在了一个名为mytest的文件夹下,我希望这个文件夹下的SVG都整合在一起,并可以方便调用,怎么办?

在svgstore: {}内部搞个自定义的配置,见下图:

意思是:mytest文件夹下的所有SVG合并成一个名为mytest.svg的大SVG文件,并放在tmp文件夹下。

我们cd到grunt-svgstore目录,然后在命令行工具中输入:

grunt

然后走起,再烧第2根香,保佑一切正常……

然后,就可以在tmp文件夹下看到我们的小结晶——mytest.svg,以及如何使用示意的HTML demo页面。

此demo页面源代码如下:

<svg xmlns="http://www.w3.org/2000/svg" style="width:0;height:0;visibility:hidden;"><symbol viewBox="0 0 1024 1024" id="iconfont-baobei"><title>iconfont-baobei</title><path fill="#272636" d="M...z" transform="translate(0, 800) scale(1, -1)"/></symbol><symbol viewBox="0 0 1024 1024" id="iconfont-bianji"><title>iconfont-bianji</title><path fill="#272636" d="M...z" transform="translate(0, 800) scale(1, -1)"/></symbol><symbol viewBox="0 0 1024 1024" id="iconfont-shangchuan"><title>iconfont-shangchuan</title><path fill="#272636" d="M...z" transform="translate(0, 800) scale(1, -1)"/></symbol></svg>

<svg>
    <use xlink:href="#iconfont-baobei"></use>
</svg>
<svg>
    <use xlink:href="#iconfont-bianji"></use>
</svg>
<svg>
    <use xlink:href="#iconfont-shangchuan"></use>
</svg>

效果如下截图:

Oh, no! 怎么有残缺,不完美,感觉不会再爱了!

淡定,小问题,貌似是这些SVG制作的时候,本身有些问题(为了font-face做的调整?),我们在illustrator中打开这些SVG, 一下子就可以看出症结所在,例如,细细的铅笔图标:

铅笔的两个角在白色的舞台之外了,正好跟上面截图少掉的两个角一致。要修复,超easy, 等比例缩放到白色舞台内就OK了,ctrl+S保存,然后重新执行下grunt命令就好了。

然后,浏览器刷新下刚刚的demo页面,当当当当,撒花撒花~~

以上~

六、关于合并:偏设计的前端看这里

注意:这里的方法,就算是CSS完全不懂的设计师也可以轻松上手!

对于一些妹子,只会写些页面,对于开发、编程的感觉比减肥还难,显然,上面的偏开发需编译的工具插件对她们就很吃力。不急不急,这里有面向设计师的方法。

你需要一个犀利的软件,矢量之王,illustrator!

一步一步跟我来:

  1. 打开软件,新建一个文件,然后另存为SVG格式;
  2. 从标尺中拉出一些参考线,搞些正方形格子,我自己用的尺寸是160*160, 矢量无尺寸,你随意;
  3. 把下载的SVG图形拖到这些格子中,缩放到合适大小。我是铺满的,这样由于对比明显,各个图标尺寸就都是一致的,CSS控制方便;

  1. 这一步很关键。打开Symbols面板,在Window菜单栏中,或Shift+Ctrl+F11启用。然后,拖动格子中的SVG图形到这个面板中,就会触发新建“元件”的行为,会打开类似下面的面板:

其中,name就是SVG中对应的symbol元素的id,因此,最好使用英文,最好易识别。下面的type你随意,这个只要在SVG导入到Flash中使用时候才有用的,这里,我们不和Flash打交道。然后,OK, 这个图标就“元件”化了,按照同样的步骤,让3个图标都变成元件(以后可重复使用),Symbols面板会类似下面这样:

  1. Ctrl+S保存,合并好的SVG即出炉。我们直接在浏览器中打开此SVG,效果不错哦~

是不是就这么结束了,太天真了。人生不如意事十之八九。我们瞅一瞅SVG的源代码,会发现,use元素居然跟小龙女一样,不干净纯洁啦——上面一大推乱七八糟的属性!

难道我们要在网页中使用如此臃肿的use元素吗?

我以小新的明义告诉你,绝对不会!

因为我昨晚在家折腾出了个工具,可以将illustrator生成SVG转换成web可用SVG Sprite.

您可以狠狠地点击这里:illustrator生成SVG转换成web可用SVG Sprite工具demo

上工具完整地址是:http://www.zhangxinxu.com/sp/svg.html 很好记忆,我站点域名+sp+svg.html, sp是special的缩写,专门放工具用的。

使用很简单:

  1. 把illustrator生成SVG所有代码拷贝到第一个框框里;
  2. 点击“转换萌萌哒”按钮,适用于SVG Sprite技术的新代码就出来了(左下的框框);同时,右侧显示了如何使用该SVG Sprite在web中真实实践;
  3. 左下角还有个红色的“导出该SVG”,就是字面意思,可以自定义名称,也可使用随机名称,为空即可;

一开始的SVG Sprite使用示意demo就是使用这个工具生成的哦~ 见下缩略图:

注意:此工具值适用于illustrator生成的SVG, 水平有限,其他SVG转换十有八九会跛掉。没怎么测试,如果发现此工具转换出了问题,欢迎提醒,定会及时修复。另外,生成的SVG文件会不定期清理,请勿外链。

我想想,还有没有什么问题……哦,为什么说此方法与CSS Sprite还要简单。

因为,CSS Sprite还需要在CSS中使用background-position一个一个地定位,哦,天哪~ 如果没有工具的话,就纯粹搬砖的苦力活啊~

但是,这里的SVG Sprite的定位,你只要在illustrator中把位置放好,illustrator这个软件就自动帮你定位好了。你只要在我的工具中转换下,生成下,然后在需要使用的地方使用:

<svg><use xlink:href="#target" /></svg>

就好了,用到CSS了吗?几乎没有,除了对SVG做尺寸限制以及改变图标的颜色。OK,这点程度的东西,小白设计师也可以轻松上手。

从这一点来看,SVG又一次成为了明日之星!设计师只要在illustrator中做好图就可以了,完全没有从前那种帮重构切图的苦逼经历了,是不是要啤酒炸鸡庆祝下!

补充于2014年7月14日

IcoMoon目前可以直接转换成SVG Sprite.

  1. 进入 http://icomoon.io/app/
  2. 点击”import icons”按钮:

  1. 选择需要的图标:

  1. 点击页面下面固定的SVG按钮:

  1. 点击打开的弹框的download按钮即可:

  1. 下载的源文件中,有两个文件夹,其中sprites文件夹中,有合并好的SVG, png图标以及对应的demo, 这个绝不会迷路的,就不截图展示了。

嘛,就此看来,这里才是老少皆宜,SVG Sprites整合最容易的地方。不过:

  • 元素还是使用的g整合,svgstore以前也是,后来改成了symbol, 这里也可能在一段时日后也会修改,但并不确定;
  • 名为sprites.html的demo页面的SVG还使用了viewbox做限制,我测试了,删掉似乎也没问题;

七、唯一的制约——兼容性

SVG图标必定是未来的趋势。为何现在国内依然不温不火,不对,应该是还没有开始有温度。我觉得除了技术学习滞后性,浏览器兼容性是最关键的制约,毕竟IE8目前依然是大头(33.23%, 刚在百度流量研究院看到的数据)。

如何破?按照我的心情,鸟它个毛线,我就手机上用用,不也挺好。静下来想想,不能意气用事,提一下IE7/IE8浏览器的处理方法吧~

有些标签,浏览器识别,会忽略里面一些东西。例如SVG的desc元素,里面的内容一向不显示的。于是,对于IE7/IE8, 我们可以把对应图标的png图片放在其中,然后IE9+等支持SVG的浏览器就会忽略之,而IE7/IE8这些不识泰山的元素就会显示图片。拿礼物这个SVG举例:

<svg class="webicon">
    <desc><img src="iconfont-baobei.png" width="16" height="16"></desc>
    <use xlink:href="#liwu"/>
</svg>兑换礼物

于是,在IE8下,就会是这样:

其他靠谱浏览器依然是这样:

于是,完美兼容了。只是活脱脱多了个标签,略败兴。当然,你也可以针对IE7/IE8使用CSS Sprite技术,原理类似,只是img标签换成其他i之类标签显示背景图,至少HTML这块会干净很多。

八、结束语

今年4月份的时候刚介绍过“CSS3图标图形生成技术”,如果说“font-face图标生成技术”是热门的话,那“CSS3图标生成技术”则属于偏门,受设计制约很大,而本文介绍的“SVG Sprite图标生成”则代表了未来。

font-face在部分win系统下,字体较小的时候,锯齿问题很讨厌,苛刻的设计师无法忍受,甚至出现了响应式font-face这样的名堂,但是,据我观察,纯正的SVG图标是没有这个问题的,而且,SVG图标具备font-face几乎所有的优点,尺寸CSS可随意定制,颜色CSS可随意定制;且没有font-face异步加载延时渲染问题;同时没有某些浏览器下font-face跨域问题;更关键的是,SVG图标支持渐变,甚至彩色图标的。而font-face实现彩色图标,要一个一个拼起来,你以为贴马赛克啊!而且,SVG中每个path元素等可以独立控制,帅气的图标变换动画等的就是你来实现(add on 2014/7/18 这个页面N多SVG图标动画)!

因此,各方面看,SVG完胜font-face,

目前唯一的问题就是兼容成本(当然,业界还是有很多成熟的优雅降级技术,可参见我之前的文章“一些SVG向下兼容优雅降级技术”)。但是,随着浏览器的发展,SVG一定会迎来自己Web新舞台!