未来必热:SVG Sprite技术介绍![](http://qr.liantu.com/api.php?text=http://www.zhangxinxu.com/wordpress/2014/07/introduce-svg-sprite-technology/)
by zhangxinxu from http://www.zhangxinxu.com
本文地址:http://www.zhangxinxu.com/wordpress/?p=4264
一、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中的<use>
元素。
三、SVG中的use元素
use元素是SVG中非常强大,非常重要的一个元素,尤其在Web开发中,为何?
两点:
- 可重复调用;
- 跨SVG调用;
1. 可重复调用
你好不容易,用了几十个坐标值,好不容易绘制了一个图形,如果你想再弄一个同样造型,但位置不同的图形出来,你会怎么办?——再复制一遍代码?别说笑了,(如果真那样)SVG文件的尺寸赶得上二师兄的腰围了。
使用<use>
元素就可以,看下面的板栗:
<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
元素(组合),从而实现了图形的重复调用功能。
2. 跨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实际应用的阻碍
两大问题:
- SVG图标从何而来?
- 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!
一步一步跟我来:
- 打开软件,新建一个文件,然后另存为SVG格式;
- 从标尺中拉出一些参考线,搞些正方形格子,我自己用的尺寸是
160*160
, 矢量无尺寸,你随意; - 把下载的SVG图形拖到这些格子中,缩放到合适大小。我是铺满的,这样由于对比明显,各个图标尺寸就都是一致的,CSS控制方便;
- 这一步很关键。打开Symbols面板,在Window菜单栏中,或Shift+Ctrl+F11启用。然后,拖动格子中的SVG图形到这个面板中,就会触发新建“元件”的行为,会打开类似下面的面板:
其中,
name
就是SVG中对应的symbol
元素的id
,因此,最好使用英文,最好易识别。下面的type
你随意,这个只要在SVG导入到Flash中使用时候才有用的,这里,我们不和Flash打交道。然后,OK, 这个图标就“元件”化了,按照同样的步骤,让3个图标都变成元件(以后可重复使用),Symbols面板会类似下面这样: - 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
的缩写,专门放工具用的。
使用很简单:
- 把illustrator生成SVG所有代码拷贝到第一个框框里;
- 点击“转换萌萌哒”按钮,适用于SVG Sprite技术的新代码就出来了(左下的框框);同时,右侧显示了如何使用该SVG Sprite在web中真实实践;
- 左下角还有个红色的“导出该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.
- 进入 http://icomoon.io/app/
- 点击”import icons”按钮:
- 选择需要的图标:
- 点击页面下面固定的SVG按钮:
- 点击打开的弹框的download按钮即可:
- 下载的源文件中,有两个文件夹,其中sprites文件夹中,有合并好的SVG, png图标以及对应的demo, 这个绝不会迷路的,就不截图展示了。
嘛,就此看来,这里才是老少皆宜,SVG Sprites整合最容易的地方。不过:
- 元素还是使用的
g
整合,svgstore以前也是,后来改成了symbol
, 这里也可能在一段时日后也会修改,但并不确定; - 名为
sprites.html
的demo页面的SVG还使用了viewbox
做限制,我测试了,删掉似乎也没问题;
补充于2014年12月21日
注意注意:SVG Sprite技术是支持外链SVG文件的,例如:
<svg viewBox="0 0 100 100"> <use xlink:href="defs.svg#icon-1"></use> </svg>
所以,很多不喜欢内联SVG的小伙伴大可放心使用该技术。但是,美中不足的是,目前,所有的IE浏览器(包括IE11)还不支持获得外链SVG文件某个元件。Chrome/FireFox/Safari/Opera等浏览器都是OK的。
补充于2015年3月18日
SVG Sprite技术是支持直接Ajax请求SVG文件字符串的。因此,对于不支持外链的IE9+浏览器,可以直接:
var ajax = new XMLHttpRequest(); ajax.open("GET", "../201407/mytest.svg", true); ajax.onload = function(e) { document.body.insertAdjacentHTML("afterBegin", '<div style="display:none;">' + ajax.responseText + '</div>'); } ajax.send();
您可以狠狠地点击这里:Ajax请求SVG文件实现Sprites效果Demo
例如,IE9浏览器下:
因此,大家大可不必担心SVG资源管理之类的问题。IE9等浏览器,一次Ajax成功后,可以直接本地存储,想想就很棒!
补充于2016年10月27日
部分华为Android手机,这种后置的ajax请求SVG写入方式无法呈现小图标,如果在页面头部一开始就有SVG文件代码资源,则没有此问题,图标不会显示不出来。
要修复此方法,可以把SVG资源作为一个JS资源载入,例如,命名一个名叫sprite.js
,里面代码大致如下:
var SVG = '<svg xmlns="http://www.w3.org/2000/svg"><symbol id="icon-arrow-l" viewBox="0 0 8 16"><path d="M.146 7.646a.5.5 0 0 0 0 .708l7 7a.5.5 0 0 0 .708-.708l-7-7v.708l7-7a.5.5 0 0 0-.708-.708l-7 7z"/></symbol><symbol id="icon-arrow-r" viewBox="0 0 7 12"><path d="M6.146 6.354v-.708l-5.5 5.5a.5.5 0 0 0 .708.708l5.5-5.5a.5.5 0 0 0 0-.708l-5.5-5.5a.5.5 0 1 0-.708.708l5.5 5.5z"/></symbol><symbol ....</symbol></svg>'; document.body.insertAdjacentHTML("afterBegin", '');
然后在页面body
标签的下面,直接:
<script src="sprite.js"></script>
类似下图:
七、唯一的制约——兼容性
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新舞台!
感谢阅读,欢迎交流,踊跃纠错,多多赞助!
本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:http://www.zhangxinxu.com/wordpress/?p=4264
(本篇完)