前言

我们处于一个用像素来作为度量衡的互联网世界中。 对于一名在互联网世界中工作的设计师和开发者来说, 像素对于我们来说亦敌亦友。 我们希望自己创作出的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能很方便的进行控制
  • 高度可压缩

在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过程中,对于SVG优化的一些技巧。这些技巧不需要花费很多额外的工作,你就能快速的你的工作中使用它。

为了使SVGs 的体积尽可能的小,你需要删除SVG中一些不需要的属性。在我看来,这方面SVGO是一个很棒的优化SVG的脚本工具。它会删除SVG中一切多余的属性 — 这里需要注意的一点的是: 当你打算用CSS / JS来控制SVG,你可能要小心了。因为使用它可能会过度优化SVG,从而影响到使用CSS和JS来控制SVG。你可以使用SVGO来自动处理SVG的优化(或者是你可能更愿意使用可视化的SVGOGUI来处理SVG的优化。

其实,删除SVG中一些多余的属性我们在矢量设计软件中就可以来处理。首先你要确保使用尽可能少的路径或者是形状来设计你想要的图形,并且路径上的控制点也要尽可能的少。并且尽可能的合并你的图层。至于删除路径上的控制点,在Illustrator中,VectorScribe这个插件就可以做到这点。它可以删除路径上多余的控制点。

优化前

Pre optimisation

删除多余的控制点

Post optimisation

如果你想放大来查看图形的一些细节。在Illustrator中,你可以通过下面的操作使用像素预览(Pixel Preview)View > Pixel Preview来查看路径上的控制点。完美像素,是保持作品的高质量必须要关注的一个事情。相信大多数设计师都会因为这个都有过被搞得疲惫不堪的经历。通过下面两张图就可以一目了然

像素偏移

Points off pixels

完美像素

Pixel snapped

如果你有两个或者是两个以上的形状,你就需要删除多余的重叠的像素。 如果有两个对齐的路径,你会看到在它们之间有一条白色的线条,所有时候你需要避免这种重叠情况的出现。

注意: SVG是严格按照定义元素的顺序来渲染的,这个与HTML靠z-index值来控制分层不一样。在SVG中,写在前面的元素先被渲染,写在后面的元素后被渲染。后渲染的元素会覆盖前面的元素,虽然有时候受透明度影响,看起来不是被覆盖的,但是SVG确实是严格按照先后顺序来渲染的。

Post optimisation

当然,最后一点重要的是要在你的网站启用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

Breaking Borders Logo

Optimised: 409b

Breaking Borders Logo

文件缩小了~71% smaller (如果使用gzip压缩的话能缩小~83%)

小技巧: Rob Sterlini提到logo中的'b'字母是重复的你可以使用<use>这个元素来重复'b'这个字幕,这样又可以减小文件的体积。

使用<use>元素来优化: 311b

Breaking Borders Logo

~78% smaller

如果你一直坚持SVGs 这些优化原则,积少成多网站的性能会稳步提升。

在网页上使用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">
Breaking Borders Logo

Background-image

使用背景图片方法需要注意的一点是,最好不要使用base64编码来格式化SVG图片,因为它在加载完前会阻塞其它资源的下载。需要注意的是,使用这种方法在交互性上有很多的限制,如不能使用JS来控制。

.logo {
  background-image: url(bblogo.svg);
}

Iframe

使用 <iframe>来加载SVGs。不过,我不确定在未来这是不是还是一种好的使用方法 ¯\_(ツ)_/¯。

<iframe src="bblogo.svg">Your browser does not support iframes</iframe>

Embed

<embed>标签,大多数浏览器都支持。但最好还是不要使用这种方法。

<embed type="image/svg+xml" src="bblogo.svg" />

Object

<object>是SVG使用方法中很好的一个选择,如果你想使用JS来进行交互控制的话。 只需要把它放到HTML中就行了。

<object type="image/svg+xml" data="bblogo.svg">Your browser does not support SVGs</object>
Your browser does not support SVGs

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 No

注意: ‘Some inline’ 意味着使用img和background-image方法来使用SVG的话,css方面会有一些限制。 但是,如果直接把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

Your browser does not support SVGs
<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> or background-image就无能为力了。 如果你使用 <object>来使用SVG代码,你需要在 SVG 中引入样式表(可以看看下面的代码)。 记住一点: 如果这样使用样式的话SVG可能会识别不了它的父类(比如<object>) 所以在使用object方法的时候,不要使用外链样式。 而直接在html文件中直接插入 SVGs 就不需要这么多顾虑了,想怎么做就怎么做。

外链样式

Your browser does not support 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; }

由于我的javascript经验不是很多,所以我这里只是分享一些如何使用使用JS 来控制SVG的小的技巧。如果你想把JS代码嵌入到SVG 文件中, 记住把代码包裹在 <![CDATA[ ... ]]> 中。如果是<img>或者是background-image,就不能用脚本来控制。

如果是使用外链 JS,如果你的 SVG 代码是直接插入到html的话,你可以像平常开发中使用JS控制DOM元素一样来控制SVG。如果是使用 <object> 你可以使用 contentDocument来控制它。比如下面:

外链脚本

Your browser does not support SVGs
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");
};

在web开发中响应式图片有两种方法,一种是制作不同固定尺寸图片,然后使用媒体查询来根据不同尺寸的设备来引用不同尺寸的图片;一种是根据图片父容器尺寸来自动调整图片自身的尺寸来适应设备。

如果是使用固定尺寸的图片,需要注意的一点是:如果是使用background-image 来使用SVG你需要使用background-size 属性来指定尺寸。因为浏览器可能不能正确解析它从而导致一些问题出现。

使用SVG 有下面几点需要注意:

Object

设置宽度 width: 100%;

Your browser does not support SVGs

Inline

以前要使用max-height才能正常的显示图片,不过现在不需要了。这里需要注意的是,如果图形很复杂,在浏览器窗口改变大小的时候,Safari可能不会立即调整图形的尺寸。

Img

设置宽度 width: 100%;

Breaking Borders Logo

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>

SVG animation

Object

Your browser does not support SVGs

Inline

Img

Breaking Borders Logo

Background-image

在web开发中,会经常遇到一些图标交互动画的场景,比如鼠标滑过图标,触发并执行动画效果。从上面的内容SVG动画支持度可以知道如果使用<img> or background-image来使用SVG的话,是不支持SVG动画的。如果是使用 SVG 动画来编写鼠标滑过动画的话,我们需要使用 begin="mouseover"begin="mouseout" 属性来触发鼠标滑过动画效果。而 CSS3 动画效果就简单多,跟我们平时使用CSS3编写动画效果一样没有什么区别,只需要使用 hover属性就可以了。有一件需要注意的事情的是, 如果你编写的动画效果用的是外链样式的话,对于直接在html文件中插入SVG代码没有什么影响;如果是使用<object>来使用SVG的话,那你也需要在SVG代码中引入你的外部样式。

SVG交互动画

只针对<object> 或者是直接在html中插入SVG代码的方式使用SVG代码有效

Your browser does not support SVGs
<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

Your browser does not support SVGs
<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 也能使用雪碧图的技术,就像平时开发中使用的PNGs来制作雪碧图一样。不过使用SVG来做雪碧图的话,一个额外的好处是我们不再需要为高清屏准备额外的2倍图。因为SVG与分辨率无关,也就是说在任何设备上SVG都能清晰的显示。并且使用SVG还能节省HTTP请求。这里有两种方法来达到雪碧图相同的效果。第一种方法是把所有的图标使用<symbol>元素来定义在 SVG 代码中并且隐藏它。然后使用<use>元素来通过<symbol>xlink:href="#id"来引用它。第二种方法是使用SVG的viewbox属性来指定显示SVG画布的区域,跟background-position原理差不多。

如果你想详细的了解SVG雪碧图的技术,可以去看看这几篇教程资源, 特别是 Sara Soueidan’s 24ways article

SVGs一个有趣的地方是,如果你在嵌入的SVG文件中的样式中使用来媒体查询,那么SVGs响应的是这些元素建立起来的viewport。也就是说,这些元素的尺寸会生成描绘SVG的viewport,也会生成CSS媒体查询条件应用的viewport。这样我们就可以使用媒体查询来告知SVG 在不同viewport下的样式。

想象一下,如果你手头有一个大品牌的logo,你想logo都能清晰的展示出来,无论用户使用的是何种尺寸的设备。使用媒体查询就能做到,根据不同尺寸的设备改变SVG图形的尺寸。这适用于所有的使用SVG的方法,除了使用background-image的方法 (备注: IE9–11只支持一个断点的设置)。滑动下面的滑块试试看会发生什么:


大部分的现代浏览器都支持SVGs。如果你还要支持诸如IE8这样的老一代的浏览器你可能需要降级来处理,对不支持SVG的浏览器使用PNGs来显示。这里就不深入讨论了,现在都啥年代了,你可能早就不需要支持像IE8 这类老一代的浏览器了 ¯\_(ツ)_/¯。 不过, 如果你真的需要降级来处理SVG的话,一些本来使用 SVG就相当简单的事情就会变得稍微复杂啦。关于降级使用SVG,建议去读读Amelia Bellamy-Royds 写的CSS-Tricks篇文章。