SVG应用指南
特别声明:本文转载于《SVG应用指南》一文,如需转载,烦请注明原文出处:https://svgontheweb.com/zh/。
前言
我们处于一个用像素来作为度量衡的互联网世界中。 对于一名在互联网世界中工作的设计师和开发者来说, 像素对于我们来说亦敌亦友。 我们希望自己创作出的Web中每一张图片、每一像素都能棱角分明(如网站中的logo
,icon
以及任何修饰性的图片),清晰的在任何设备上展现给用户的同时, 也能尽可能的保证图片的体积来优化性能。而SVG就是解决上面问题非常棒的一个方案。SVG 意为可缩放矢量图形(Scalable Vector Graphics)与屏幕分辨率无关, 体积上能使用Gzip的方式压缩, 而且修改编辑都很方便。
本文是SVG在Web开发中一些常见的应用场景、技巧以及使用方法的一个简单的入门指南。
SVG
SVG是一种用 XML 定义的语言,用来描述二维矢量及矢量/栅格图形。 比如常用的矢量设计软件Illustrator中的形状、路径,或者是用Illustrator创作的任何图形都是矢量图形。 SVG是跟Web中常用的JPEG一样是一种的图形格式。 但是SVG比其它图形格式有一个很突出的特点,就是能使用CSS / JS等Web技术对它进行控制。
- 与分辨率无关
- 被现代浏览器支持
- 面向未来 (W3C 标准)
- 容易编辑
- 使用CSS 和 JS能很方便的进行控制
- 高度可压缩
使用和优化SVG
在Web中使用 SVG 就像使用JPEG 或者 PNG一样很简单。 使用常见的矢量设计软件(Illustrator,Sketch,或者是PhotoShop中的形状图层) 就能设计出SVG格式的图形。下面所说的操作都是以Illustrator软件为例的(在其它矢量设计软件中可能有所不同)。如果你想要在你的矢量图形中导出特殊的文字,它可能在Web中不会显示正确的字体,除非你使用CSS3中的Webfont来指定显示特定的字体。在Illustrator中,扩展对象可用来将单一对象分割为若干个对象。具体的来说,如果你想单独的控制每一个对象,那就可以使用扩展对象。并且扩展对象并不会影响导出SVG的文件体积。在Illustrator中每一个图层的名字在导出SVG的时候,都会在 SVG作为元素的ID的名字,这对于使用CSS或者是JS来控制SVG提供了很大的便利,但会增加SVG文件的体积哦。
在导出的时候, 需要检查下画布的尺寸(比如,不要出现 23.3px × 86.8px
) 这样有小数点的格式,如果这样的话它可能会对画布里的内容进行剪裁。在Illustrator,你可以通过下面的操作来避免这个问题,在菜单栏进行下面的操作Object > Artboards > Fit to Artwork Bounds
。然后选择 保存为 命令选择保存为 SVG 格式,选择默认的选项就可以了。 当然,在选择保存SVG选项里,也可以进行一定的优化。后面会就SVG的优化有专门的阐述。
SVG文件体积优化的一些技巧
关于SVG优化,网络上有很多的关于这方面的文章。这里我更愿意来分享我在使用SVG过程中,对于SVG优化的一些技巧。这些技巧不需要花费很多额外的工作,你就能快速的你的工作中使用它。
为了使SVGs 的体积尽可能的小,你需要删除SVG中一些不需要的属性。在我看来,这方面SVGO是一个很棒的优化SVG的脚本工具。它会删除SVG中一切多余的属性 —— 这里需要注意的一点的是: 当你打算用CSS / JS来控制SVG,你可能要小心了。因为使用它可能会过度优化SVG,从而影响到使用CSS和JS来控制SVG。你可以使用SVGO来自动处理SVG的优化(或者是你可能更愿意使用可视化的SVGO GUI来处理SVG的优化。
其实,删除SVG中一些多余的属性我们在矢量设计软件中就可以来处理。首先你要确保使用尽可能少的路径或者是形状来设计你想要的图形,并且路径上的控制点也要尽可能的少。并且尽可能的合并你的图层。至于删除路径上的控制点,在Illustrator中,VectorScribe这个插件就可以做到这点。它可以删除路径上多余的控制点。
这里推荐一些关于SVG优化的资源。
优化前
优化后
如果你想放大来查看图形的一些细节。在Illustrator中,你可以通过下面的操作使用像素预览(Pixel Preview)View > Pixel Preview
来查看路径上的控制点。完美像素,是保持作品的高质量必须要关注的一个事情。相信大多数设计师都会因为这个都有过被搞得疲惫不堪的经历。通过下面两张图就可以一目了然:
像素偏移
完美像素
如果你有两个或者是两个以上的形状,你就需要删除多余的重叠的像素。 如果有两个对齐的路径,你会看到在它们之间有一条白色的线条,所有时候你需要避免这种重叠情况的出现。
注意: SVG是严格按照定义元素的顺序来渲染的,这个与HTML靠z-index值来控制分层不一样。在SVG中,写在前面的元素先被渲染,写在后面的元素后被渲染。后渲染的元素会覆盖前面的元素,虽然有时候受透明度影响,看起来不是被覆盖的,但是SVG确实是严格按照先后顺序来渲染的。
当然,最后一点重要的是要在你的网站启用gzip技术来压缩SVGs。这需要在你网站的htaccess
文件中设置一下。
AddType image/svg+xml svg svgz
<IfModule mod_deflate.c>
<IfModule mod_filter.c>
AddOutputFilterByType DEFLATE "image/svg+xml" \
"text/css" \
"text/html" \
"text/javascript"
... etc
</IfModule>
</IfModule>
上面说的这些优化方法,在 Breaking Borders 网站上的logo
(svg
格式)就是这样做的: 尽可能的压缩文件, 删除路径中多余的控制点, 对齐像素, 删除重叠的部分,然后使用 SVGO来优化。
Original: 1,413b
Optimised: 409b
文件缩小了~71%
smaller (如果使用gzip压缩的话能缩小~83%
)
小技巧: Rob Sterlini提到
logo
中的b
字母是重复的你可以使用<use>
这个元素来重复b
这个字幕,这样又可以减小文件的体积。
使用<use>
元素来优化: 311b
~78% smaller
如果你一直坚持SVGs 这些优化原则,积少成多网站的性能会稳步提升。
SVG使用方法
在网页上使用SVGs 有不同的使用方法。对于这些使用方法要具体问题具体分析,因地制宜的采用。有些使用方法是要避免使用的。如果,你只是想使用SVG跟分辨率无关和文件体积这两个特性,你可以使用img或者是使用CSS中background-image
来引入SVG格式的图形,就像使用其它的图形格式一样简单。
Img
仅仅在img
标签中引入svg
就可以了。你也可以在<picture>
元素中使用SVGs。需要注意的是,使用这种方法在交互性上有很多的限制,如不能使用JS来控制。
<img src="bblogo.svg" alt="Breaking Borders Logo" height="65" width="68">
Background-image
使用背景图片方法需要注意的一点是,最好不要使用base64
编码来格式化SVG图片,因为它在加载完前会阻塞其它资源的下载。需要注意的是,使用这种方法在交互性上有很多的限制,如不能使用JS来控制。
.logo {
background-image: url(bblogo.svg);
}
Iframe
使用 <iframe>
来加载SVGs。不过,我不确定在未来这是不是还是一种好的使用方法。
Embed
<embed>
标签,大多数浏览器都支持。但最好还是不要使用这种方法。
Object
<object>
是SVG使用方法中很好的一个选择,如果你想使用JS来进行交互控制的话。 只需要把它放到HTML中就行了。
<object type="image/svg+xml" data="bblogo.svg">Your browser does not support SVGs</object>
Inline
把 SVG直接插入到html
中,可以节省 HTTP
请求,而且很方便实用JS来控制。但是,也意味着图片不能被浏览器缓存。同时使用JS来操控SVG也意味着浏览器会发生重绘行为。
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 68 65">
<path fill="#1A374D" d="M42 27v-20c0-3.7-3.3-7-7-7s-7 3.3-7 7v21l12 15-7 15.7c14.5 13.9 35 2.8 35-13.7 0-13.3-13.4-21.8-26-18zm6 25c-3.9 0-7-3.1-7-7s3.1-7 7-7 7 3.1 7 7-3.1 7-7 7z"/>
<path d="M14 27v-20c0-3.7-3.3-7-7-7s-7 3.3-7 7v41c0 8.2 9.2 17 20 17s20-9.2 20-20c0-13.3-13.4-21.8-26-18zm6 25c-3.9 0-7-3.1-7-7s3.1-7 7-7 7 3.1 7 7-3.1 7-7 7z"/>
</svg>
总结
如果你想尽可能使用发挥SVGs的特性,那使用<object>
来使用SVG是最好的方法。 当然,你也可以直接把SVG代码直接插入到html
文件中来节省HTTP
请求, 要记住的是,它就不能被缓存了哦。 如果仅仅是想像使用其他格式图片的方法来使用SVG,那就要使用<img>
或者是 background-image
方法。 也可以使用 <iframe>
和 <embed>
方法来使用SVG,不过我觉得这两个方法不是好的选择。
Object | Inline | Img | Background-image | |
---|---|---|---|---|
CSS Manipulation | Yes | Yes | Some inline | Some inline |
JS Manipulation | Yes | Yes | No | No |
SVG Animation | Yes | Yes | Yes | Yes |
Interactive SVG | Animation | Yes | Yes | No |
注意: ‘Some inline’ 意味着使用
img
和background-image
方法来使用SVG的话,CSS方面会有一些限制。 但是,如果直接把CSS写在SVG代码里面情况就不一样了。更多关于CSS与SVG的信息在下一节阐述。
CSS 与SVG
使用SVG 值得庆幸的事情是我们依然可以使用CSS来控制SVG的表现。比如,我们想要一个icon
在一些地方显示蓝色,我们只需要改变它的颜色即可而不需要换一张新的蓝色的图片。
有两种方法来使用样式 —— 内联样式和外链样式。内联样式,即把样式包裹在 <style>
标签里然后放到<![CDATA[ ... ]]>
里面。一定要记得把样式包裹在<![CDATA[ ... ]]>
里面,因为有时候 XML在解析一些特殊字符串的时候会有一些问题(比如>
)。即使你没有一些特殊的字符串,也建议你使用上面的方法来写SVG的内联样式。即使用CDATA
标签来包裹内联样式。
使用内联样式来操作SVG是一个非常好的实践。像这种<img>
和 background-image
内联插入图片到话是不支持CSS3动画的 (可以看看 animations
部分了解更多的关于动画方面的)。 background-image
不支持媒体查询的(关于媒体查询可以先看看 media queries
这部分了解更多的关于SVG与媒体查询)。而如果把样式内联在SVG文件中就可以解决这两个问题。
Inline Styles
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 68 65">
<style type="text/css">
<![CDATA[
.firstb { fill: yellow; }
.secondb { fill: red; }
]]>
</style>
<path class="secondb" d="M42 27v-20c0-3.7-3.3-7-7-7s-7 3.3-7 7v21l12 15-7 15.7c14.5 13.9 35 2.8 35-13.7 0-13.3-13.4-21.8-26-18zm6 25c-3.9 0-7-3.1-7-7s3.1-7 7-7 7 3.1 7 7-3.1 7-7 7z"/>
<path class="firstb" d="M14 27v-20c0-3.7-3.3-7-7-7s-7 3.3-7 7v41c0 8.2 9.2 17 20 17s20-9.2 20-20c0-13.3-13.4-21.8-26-18zm6 25c-3.9 0-7-3.1-7-7s3.1-7 7-7 7 3.1 7 7-3.1 7-7 7z"/>
</svg>
如果使用外链样式的话也很容易来操作SVG图形,不过对于 <img>
或者 background-image
就无能为力了。 如果你使用 <object>
来使用SVG代码,你需要在 SVG 中引入样式表(可以看看下面的代码)。 记住一点: 如果这样使用样式的话SVG可能会识别不了它的父类(比如<object>
) 所以在使用object
方法的时候,不要使用外链样式。 而直接在html
文件中直接插入 SVGs 就不需要这么多顾虑了,想怎么做就怎么做。
外链样式
// Add to very start of SVG file before <svg>
<?xml-stylesheet type="text/css" href="style.css"?>
// In style.css
.firstb {
fill: yellow;
}
.secondb {
fill: red;
}
JS与SVG
由于我的JavaScript经验不是很多,所以我这里只是分享一些如何使用使用JS 来控制SVG的小的技巧。如果你想把JS代码嵌入到SVG 文件中, 记住把代码包裹在 <![CDATA[ ... ]]>
中。如果是<img>
或者是background-image
,就不能用脚本来控制。
如果是使用外链 JS,如果你的 SVG 代码是直接插入到html
的话,你可以像平常开发中使用JS控制DOM元素一样来控制SVG。如果是使用 <object>
你可以使用 contentDocument
来控制它。比如下面:
window.onload=function() {
var object = document.getElementById("logoObject");
var svgDocument = object.contentDocument;
var svgb1 = svgDocument.getElementsByClassName("firstb");
var svgb2 = svgDocument.getElementsByClassName("secondb");
svgb1[0].setAttribute("fill", "yellow");
svgb2[0].setAttribute("fill", "red");
};
SVG与响应式
在Web开发中响应式图片有两种方法,一种是制作不同固定尺寸图片,然后使用媒体查询来根据不同尺寸的设备来引用不同尺寸的图片;一种是根据图片父容器尺寸来自动调整图片自身的尺寸来适应设备。
如果是使用固定尺寸的图片,需要注意的一点是:如果是使用background-image
来使用SVG你需要使用background-size
属性来指定尺寸。因为浏览器可能不能正确解析它从而导致一些问题出现。
使用SVG 有下面几点需要注意:
Object
设置宽度 width: 100%;
:
Inline
以前要使用max-height
才能正常的显示图片,不过现在不需要了。这里需要注意的是,如果图形很复杂,在浏览器窗口改变大小的时候,Safari可能不会立即调整图形的尺寸。
Img
设置宽度 width: 100%;
:
Background-image
需要设置 padding-bottom: #%;
来使图片能正常的显示。
动画
SVGs 动画方法给我们的选择很多:比如SVG本身的SVG动画(基于 SMIL)、CSS3动画、或者是JS 动画。使用 SVG 动画和 CSS3 动画就可以做出很多的动画效果,而使用SVG 动画更是CSS3动画不可比拟的。而使用JavaScript则能使我们编写更加复杂的动画效果,比如 Snap.svg就是一个专门用来操控SVG的JavaScript库。这里就不详细说这个JS库了,可以去它的官网看看它们的实例。
SVG动画非常的强大。不过这里我并不会展开来说,因为我使用SVG动画的经验也比较少。不过当我了解它到强大之后,我可以想象它能制作出很强大的动画效果。唯一不足的是可能要花费点时间。如果你有兴趣的话,这里推荐一些非常不错的教程 地址 。 SVG动画简而言之就是在SVG代码中插入 <animate>
元素,从而可以给路径或者是形状添加动画效果。大部分浏览器都支持SVG动画,不过 Internet Explorer不支持 SVG 动画, 当然你如果想在IE上也运行动画效果,可以使用FakeSmile这个JavaScript库来支持IE。
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 68 65">
<path fill="#4e86b1" d="M42 27v-20c0-3.7-3.3-7-7-7s-7 3.3-7 7v21l12 15-7 15.7c14.5 13.9 35 2.8 35-13.7 0-13.3-13.4-21.8-26-18zm6 25c-3.9 0-7-3.1-7-7s3.1-7 7-7 7 3.1 7 7-3.1 7-7 7z">
<animate dur="2s" values="#000000; #4e86b1; #000000" keyTimes="0; 0.5; 1" attributeName="fill" repeatCount="indefinite"/>
</path>
<path d="M14 27v-20c0-3.7-3.3-7-7-7s-7 3.3-7 7v41c0 8.2 9.2 17 20 17s20-9.2 20-20c0-13.3-13.4-21.8-26-18zm6 25c-3.9 0-7-3.1-7-7s3.1-7 7-7 7 3.1 7 7-3.1 7-7 7z">
<animate dur="2s" values="#4e86b1; #000000; #4e86b1" keyTimes="0; 0.5; 1" attributeName="fill" repeatCount="indefinite"/>
</path>
</svg>
下面有一个简单的实例:两个字母之间的颜色互相切换:
在Web开发中,会经常遇到一些图标交互动画的场景,比如鼠标滑过图标,触发并执行动画效果。从上面的内容SVG动画支持度可以知道如果使用<img>
或者 background-image
来使用SVG的话,是不支持SVG动画的。如果是使用 SVG 动画来编写鼠标滑过动画的话,我们需要使用 begin="mouseover"
和 begin="mouseout"
属性来触发鼠标滑过动画效果。而 CSS3 动画效果就简单多,跟我们平时使用CSS3编写动画效果一样没有什么区别,只需要使用 hover
属性就可以了。有一件需要注意的事情的是, 如果你编写的动画效果用的是外链样式的话,对于直接在html
文件中插入SVG代码没有什么影响;如果是使用<object>
来使用SVG的话,那你也需要在SVG代码中引入你的外部样式。
SVG交互动画
只针对<object>
或者是直接在html
中插入SVG代码的方式使用SVG代码有效:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 68 65">
<path fill="#4e86b1" d="M42 27v-20c0-3.7-3.3-7-7-7s-7 3.3-7 7v21l12 15-7 15.7c14.5 13.9 35 2.8 35-13.7 0-13.3-13.4-21.8-26-18zm6 25c-3.9 0-7-3.1-7-7s3.1-7 7-7 7 3.1 7 7-3.1 7-7 7z">
<animate fill="freeze" dur="0.1s" to="#000000" from="#4e86b1" attributeName="fill" begin="mouseover"/>
<animate fill="freeze" dur="0.1s" to="#4e86b1" from="#000000" attributeName="fill" begin="mouseout"/>
</path>
<path d="M14 27v-20c0-3.7-3.3-7-7-7s-7 3.3-7 7v41c0 8.2 9.2 17 20 17s20-9.2 20-20c0-13.3-13.4-21.8-26-18zm6 25c-3.9 0-7-3.1-7-7s3.1-7 7-7 7 3.1 7 7-3.1 7-7 7z">
<animate fill="freeze" dur="0.1s" to="#4e86b1" from="#000000" attributeName="fill" begin="mouseover"/>
<animate fill="freeze" dur="0.1s" to="#000000" from="#4e86b1" attributeName="fill" begin="mouseout"/>
</path>
</svg>
CSS3 动画
只针对 <object>
或者是直接在html
中插入SVG代码的方法使用SVG。使用内联样式的方式来编写动画效果previously。
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 68 65">
<style type="text/css">
<![CDATA[
.firstb { fill: #000; transition: fill 0.1s; }
.firstb:hover { fill: #4e86b1; }
.secondb { fill: #4e86b1; transition: fill 0.1s; }
.secondb:hover { fill: #000; }
]]>
</style>
<path class="secondb" d="M42 27v-20c0-3.7-3.3-7-7-7s-7 3.3-7 7v21l12 15-7 15.7c14.5 13.9 35 2.8 35-13.7 0-13.3-13.4-21.8-26-18zm6 25c-3.9 0-7-3.1-7-7s3.1-7 7-7 7 3.1 7 7-3.1 7-7 7z"/>
<path class="firstb" d="M14 27v-20c0-3.7-3.3-7-7-7s-7 3.3-7 7v41c0 8.2 9.2 17 20 17s20-9.2 20-20c0-13.3-13.4-21.8-26-18zm6 25c-3.9 0-7-3.1-7-7s3.1-7 7-7 7 3.1 7 7-3.1 7-7 7z"/>
</svg>
SVG与雪碧图
SVG 也能使用雪碧图的技术,就像平时开发中使用的PNGs来制作雪碧图一样。不过使用SVG来做雪碧图的话,一个额外的好处是我们不再需要为高清屏准备额外的2倍图。因为SVG与分辨率无关,也就是说在任何设备上SVG都能清晰的显示。并且使用SVG还能节省HTTP请求。这里有两种方法来达到雪碧图相同的效果。第一种方法是把所有的图标使用<symbol>
元素来定义在 SVG 代码中并且隐藏它。然后使用<use>
元素来通过<symbol>
的 xlink:href="#id"
来引用它。第二种方法是使用SVG的viewbox
属性来指定显示SVG画布的区域,跟background-position
原理差不多。
如果你想详细的了解SVG雪碧图的技术,可以去看看这几篇教程资源, 特别是 Sara Soueidan’s 在24ways网站上的文章。
媒体查询
SVGs一个有趣的地方是,如果你在嵌入的SVG文件中的样式中使用来媒体查询,那么SVGs响应的是这些元素建立起来的viewport
。也就是说,这些元素的尺寸会生成描绘SVG的viewport
,也会生成CSS媒体查询条件应用的viewport
。这样我们就可以使用媒体查询来告知SVG 在不同viewport
下的样式。
想象一下,如果你手头有一个大品牌的logo
,你想logo
都能清晰的展示出来,无论用户使用的是何种尺寸的设备。使用媒体查询就能做到,根据不同尺寸的设备改变SVG图形的尺寸。这适用于所有的使用SVG的方法,除了使用background-image
的方法 (备注: IE9–11只支持一个断点的设置)。滑动下面的滑块试试看会发生什么:
降级使用SVG
大部分的现代浏览器都支持SVGs。如果你还要支持诸如IE8这样的老一代的浏览器你可能需要降级来处理,对不支持SVG的浏览器使用PNGs来显示。这里就不深入讨论了,现在都啥年代了,你可能早就不需要支持像IE8 这类老一代的浏览器了。 不过, 如果你真的需要降级来处理SVG的话,一些本来使用 SVG就相当简单的事情就会变得稍微复杂啦。关于降级使用SVG,建议去读读Amelia Bellamy-Royds 写的CSS-Tricks篇文章。
推荐资源
SVG基本介绍
- Ultimate guide to SVG
- How to Add SVGs to Your Web Page
- W3C About SVG
- Rethinking Responsive SVG
- Making SVGs Responsive
- Using SVG
- SVG Styling
SVG优化
- Tips For Optimising SVG Delivery For The Web
- How To Optimize SVG
- Understanding and Manually Improving SVG Optimization
- SVG Optimization Video
- SVGOMG — SVGO GUI in browser
SVG动画
- A Guide to SVG Animations
- Styling And Animating SVGs With CSS
- SVG animations for fun and profit
- Snap.svg
- SVG.js
- Pablo