SVG 格式在前端领域越来越流行,它的特性使得各种高清屏幕都不会失真。于是我收藏一篇个人觉得比较好的 SVG 文章。
转自网易考拉前端团队博客:http://blog.kaolafed.com/2017/04/25/SVG%20Sprite%20%E4%BD%BF%E7%94%A8%E7%AE%80%E4%BB%8B/
SVG简介
SVG即可缩放矢量图形 (Scalable Vector Graphics)的简称, 是一种用来描述二维矢量图形的XML标记语言. SVG图形不依赖于分辨率, 因此图形不会因为放大而显示出明显的锯齿边缘.
icon sprite
当我们需要使用多个icon的时候, 为了节省请求和方便管理, 通常会把icon合并到一个文件中, 在使用时再通过一定的方法从icon集合文件中取出所需的图形并显示. 目前使用得最多的应该就是我们所熟悉的CSS Sprite和Icon Font.
CSS Sprite
CSS Sprite的原理是将多个icon按一定规律整理到一个图片文件中, 使用时利用background-image
和background-position
将图片中特定部分显示出来. CSS Sprite技术已经被广泛应用了很长的一段时间, 目前有许多自动化生成Sprite图片和CSS文件的工具, 例如(gulp.spritesmith)[https://github.com/twolfson/gulp.spritesmith].
.icon1 {
background-image: url(/res/icon1.png)
}
.icon1-increase {
background-position: -10px -10px;
}
CSS Sprite技术成熟, 兼容性好, 但是缺点也比较明显. 如在实际需求中, 对应形状相同但颜色不同的icon, 就需要为不同颜色的icon各保存一份; 有时候需要对已有icon放大显示时, 发现锯齿严重, 那么又要再保存一份放大版的icon. 因此, Sprite文件会随着时间越变越大, 同时内容越来越乱, 逐渐变得难以管理.
Icon Font
Icon Font的基本原理是将Icon定义为图片字体, 在CSS中用@font-face
引入Icon Font自定义字体, 再利用font-family
和字符码显示出指定的图标.
@font-face {
font-family: 'iconfont';
src: url(/res/icon2.ttf) format('truetype');
}
.icon2 {
font-family: 'iconfont';
}
!
由于使用的是字体, 因此可以通过color
, font-size
设置icon的样式. Icon Font拥有比CSS Sprite图片更小的文件体积, 维护也比图片更方便, 但是icon font通常只能使用单一的颜色, 字体文件生成也比CSS Sprite更复杂.
SVG Sprite
通常在使用SVG的时候, 我们是直接写到svg
标签当中:
此时SVG图形会直接在页面当中显示. SVG属性中, 可以利用(symbol
)[https://developer.mozilla.org/zh-CN/docs/Web/SVG/Element/symbol]封装图形, 并利用(use
)[https://developer.mozilla.org/zh-CN/docs/Web/SVG/Element/use]将其实例化, 从而实现SVG Sprite的功能.
SVG Sprite实例:
这样就实现了SVG Sprite.
原理
通过devtool可以观察到, use
标签是利用shadow dom实现的.
它通过xlink:href
这个XML的attribute
引用指定的SVG symbol
, 在渲染时指定symbol
标签中的内容就会被渲染显示在页面当中. 这意味着, 如果无法直接对use
标签中的shadow dom进行访问和修改. 例如像use#rect
这样的选择器是无法生效的, 因此不能通过一般的css选择器对use
中图形不同的部分进行控制.
CSS样式
更多的时候, 我们通常都只需要改变图标的大小和颜色, 在SVG Sprite当中, 实现起来并不复杂.
- 大小
通过改变svg
容器的大小, 可以轻松地对图标大小进行控制.
.icon{
width: 120px;
height: 80px;
}
.icon.icon-small{
width: 60px;
height: 40px;
}
- 颜色
- 单色
颜色由于不能直接对use
的shadow root中的图形标签进行选择, 因此在为图标定义颜色时需要利用fill: inherit
这个一属性.
svg path{
fill: inherit;
}
.icon2-green{
fill: #008d46;
}
.icon2-red{
fill: #dc352f;
}
利用inherit
, 还可以定义stroke-width
, stroke
等属性.
- 多色
除了对图标整体颜色进行定义以外, 还可以根据需要对图形不同部分进行颜色定义. 这里使用到了css 的自定义属性(CSS Custom Properties).
这里需要对icon.svg
进行修改, 将图形个部分的颜色设置为fill: var(--*[, default])
的形式.
定义css样式
.flag-belgium {
--color0: #201b18;
--color1: #f1ee3d;
--color2: #dc352f;
}
除此以外, 还有其他一些样式定义的形式, 更详细的内容可以阅读Styling SVG
实际使用
引用外部svg
上文介绍的是使用内联svg, 但其实还可以使用外部svg的.
然而这种方法不兼容IE9~10, 但如果非要使用外部svg的形式, 可以引入一个pollyfillsvg4everybody, 当运行在不支持外部svg的情况下, 这个pollyfill会通过异步请求加载svg委文件并将其注入到页面当中, 将引用方法转换成内联svg.
在ftl中使用
可以在svg.ftl
中定义一系列macro
, 用以加载.svg
文件.
<#macro svgicon path>
<#include "${path}">
#macro>
<#macro svgicon3 >
<@svgicon "./icon3.svg"/>
#macro>
在页面中引用demo.ftl
:
<@svgicon1/>
在regular中使用
xlink:href
是使用了命名空间的XML特性, 如果是写在.html
页面的标签, 该特性能够正常被浏览器解析并完成svg渲染. 如果该svg变量是通过DOM API创建出来的话, 则需要使用特定的方法进行处理(SVG with USE tag not rendering).
即需要利用createElementNS
和setAttributeNS
方法在创建的同时声明命名空间.
var use = document.createElementNS('http://www.w3.org/2000/svg', 'use');
use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#icon-increase');
document.querySelector('#svgid').appendChild(use);
只要是需要动态创建use
元素, 都需要使用上面这种方法才能使SVG Sprite中的元素实例化. 但在目前的Regular(0.4.3)中, 不会对命名空间进行处理, 这意味着, 如果直接将写在Regular组件的模版中时, 该标签是无法正常渲染的. 为此可以增加一个指令
r-xlink:href
以完成手动设置命名空间的操作.
Regular.directive('r-xlink:href', function (elem, val) {
if (val&& val.type === 'expression') {
this.$watch(val, function (newVal) {
elem.setAttributeNS('http://www.w3.org/1999/xlink', 'href', newVal);
});
} else {
elem.setAttributeNS('http://www.w3.org/1999/xlink', 'href', val);
}
});
那么在组件模版中就可以像在普通.html
中那样使用SVG Sprite了. 当然, 首先得确保SVG Sprite被写到页面中.
在Regular的SVG 实践中得到了郑海波大神的指点, 否则得绕更大的路才能把问题解决, 在此表示感谢.
小结
对比前文提到的CSS Sprite和Icon Font, SVG有着明显的优势:
- 放大缩小不会失真
- 大小, 颜色等属性自定义灵活
- 体积小, 同时管理方便
虽然SVG Sprite有着高度的灵活性, 但于此同时, SVG兼容性有待考究, 同时其渲染性能也不及图片和字体那么高, 可能在某些情况下不适用. 不过在一般的场景中, svg sprite还能够给开发带来很大的便利的.
参考
- SVG元素参考
- SVG with USE tag not rendering
- 未来必热:SVG Sprite技术介绍
- Styling SVG
- Icon System with SVG Sprites