SVG Sprite 使用简介

@elcarim5efil 2017-04-25 03:02:33发表于 kaola-fed/blog 已归档

SVG简介

SVG即可缩放矢量图形 (Scalable Vector Graphics)的简称, 是一种用来描述二维矢量图形的XML标记语言. SVG图形不依赖于分辨率, 因此图形不会因为放大而显示出明显的锯齿边缘.

icon sprite

当我们需要使用多个icon的时候, 为了节省请求和方便管理, 通常会把icon合并到一个文件中, 在使用时再通过一定的方法从icon集合文件中取出所需的图形并显示. 目前使用得最多的应该就是我们所熟悉的CSS Sprite和Icon Font.

CSS Sprite

CSS Sprite的原理是将多个icon按一定规律整理到一个图片文件中, 使用时利用background-imagebackground-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;
}
<i class="icon1 icon1-increase"/>

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';
}
<i class="icon2">&#33</i>

由于使用的是字体, 因此可以通过color, font-size设置icon的样式. Icon Font拥有比CSS Sprite图片更小的文件体积, 维护也比图片更方便, 但是icon font通常只能使用单一的颜色, 字体文件生成也比CSS Sprite更复杂.

SVG Sprite

通常在使用SVG的时候, 我们是直接写到svg标签当中:

    <svg xmlns="http://www.w3.org/2000/svg" width="150" height="100" viewBox="0 0 3 2">
        <rect width="1" height="2" x="0" fill="#008d46" />
        <rect width="1" height="2" x="1" fill="#ffffff" />
        <rect width="1" height="2" x="2" fill="#d2232c" />
    </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 style="height:0;width:0;display:none;" version="1.1" xmlns="http://www.w3.org/2000/svg">
    <symbol id="icon-italy" width="150" height="100" viewBox="0 0 3 2">
        <rect width="1" height="2" x="0" fill="#008d46" />
        <rect width="1" height="2" x="1" fill="#ffffff" />
        <rect width="1" height="2" x="2" fill="#d2232c" />
    </symbol>

    <symbol id="icon-france" width="150" height="100" viewBox="0 0 3 2">
        <rect width="1" height="2" x="0" fill="#002496" />
        <rect width="1" height="2" x="1" fill="#ffffff" />
        <rect width="1" height="2" x="2" fill="#ee2839" />
    </symbol>
</svg>

<svg><use xlink:href="#icon-italy"/></svg>
<svg><use xlink:href="#icon-france"/></svg>

这样就实现了SVG Sprite.

原理

通过devtool可以观察到, use标签是利用shadow dom实现的.

use.shadowdom

它通过xlink:href这个XML的attribute引用指定的SVG symbol, 在渲染时指定symbol标签中的内容就会被渲染显示在页面当中. 这意味着, 如果无法直接对use标签中的shadow dom进行访问和修改. 例如像use#rect这样的选择器是无法生效的, 因此不能通过一般的css选择器对use中图形不同的部分进行控制.

CSS样式

更多的时候, 我们通常都只需要改变图标的大小和颜色, 在SVG Sprite当中, 实现起来并不复杂.

  1. 大小

通过改变svg容器的大小, 可以轻松地对图标大小进行控制.

.icon{
    width: 120px;
    height: 80px;
}
.icon.icon-small{
    width: 60px;
    height: 40px;
}
<svg class="icon"><use xlink:href="#icon-italy"/></svg>
<svg class="icon icon-small"><use xlink:href="#icon-italy"/></svg>

svg.icon.size

  1. 颜色
  • 单色

颜色由于不能直接对use的shadow root中的图形标签进行选择, 因此在为图标定义颜色时需要利用fill: inherit这个一属性.

svg path{
    fill: inherit;
}
.icon2-green{
    fill: #008d46;
}
.icon2-red{
    fill: #dc352f;
}
<svg class="icon2 icon2-green">
    <use xlink:href="#icon-increase"/>
</svg>
<svg class="icon2 icon2-red">
    <use xlink:href="#icon-increase"/>
</svg>

svg.icon.color

利用inherit, 还可以定义stroke-width, stroke等属性.

  • 多色

除了对图标整体颜色进行定义以外, 还可以根据需要对图形不同部分进行颜色定义. 这里使用到了css 的自定义属性(CSS Custom Properties).

这里需要对icon.svg进行修改, 将图形个部分的颜色设置为fill: var(--*[, default])的形式.

<symbol id="icon-flag" width="150" height="100" viewBox="0 0 3 2">
    <rect width="1" height="2" x="0" style="fill: var(--color0, #008d46)" />
    <rect width="1" height="2" x="1" style="fill: var(--color1, #fff)"/>
    <rect width="1" height="2" x="2" style="fill: var(--color2, #d2232c)"/>
</symbol>

定义css样式

.flag-belgium {
    --color0: #201b18;
    --color1: #f1ee3d;
    --color2: #dc352f;
}
<svg class="icon">
    <use xlink:href="#icon-flag"/>
</svg>
<svg class="icon flag-belgium">
    <use xlink:href="#icon-flag"/>
</svg>

svg.icon.color.parts

除此以外, 还有其他一些样式定义的形式, 更详细的内容可以阅读Styling SVG Content with CSS.

实际使用

引用外部svg

上文介绍的是使用内联svg, 但其实还可以使用外部svg的.

<svg class="icon">
    <use xlink:href="/res/svg/icon.svg#icon-flag"/>
</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/>
<svg>
    <use xlink:href="#icon-italy"/>
</svg>

在regular中使用

xlink:href是使用了命名空间的XML特性, 如果是写在.html页面的标签, 该特性能够正常被浏览器解析并完成svg渲染. 如果该svg变量是通过DOM API创建出来的话, 则需要使用特定的方法进行处理(SVG with USE tag not rendering).

即需要利用createElementNSsetAttributeNS方法在创建的同时声明命名空间.

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)中, 不会对命名空间进行处理, 这意味着, 如果直接将<svg><use xlink:href/></svg>写在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被写到页面中.

<svg>
    <use r-xlink:href="#icon-italy"/>
</svg>

在Regular的SVG 实践中得到了郑海波大神的指点, 否则得绕更大的路才能把问题解决, 在此表示感谢.

小结

对比前文提到的CSS Sprite和Icon Font, SVG有着明显的优势:

  • 放大缩小不会失真
  • 大小, 颜色等属性自定义灵活
  • 体积小, 同时管理方便

虽然SVG Sprite有着高度的灵活性, 但于此同时, SVG兼容性有待考究, 同时其渲染性能也不及图片和字体那么高, 可能在某些情况下不适用. 不过在一般的场景中, svg sprite还能够给开发带来很大的便利的.

参考

  1. SVG元素参考
  2. SVG with USE tag not rendering
  3. 未来必热:SVG Sprite技术介绍
  4. Styling SVG Content with CSS
  5. Icon System with SVG Sprites