SVG 图像入门教程

作者: 阮一峰

日期: 2018年8月 6日

珠峰培训

一、概述

SVG 是一种基于 XML 语法的图像格式,全称是可缩放矢量图(Scalable Vector Graphics)。其他图像格式都是基于像素处理的,SVG 则是属于对图像的形状描述,所以它本质上是文本文件,体积较小,且不管放大多少倍都不会失真。

SVG 文件可以直接插入网页,成为 DOM 的一部分,然后用 JavaScript 和 CSS 进行操作。


<!DOCTYPE html>
<html>
<head></head>
<body>
<svg
  id="mysvg"
  xmlns="http://www.w3.org/2000/svg"
  viewBox="0 0 800 600"
  preserveAspectRatio="xMidYMid meet"
>
  <circle id="mycircle" cx="400" cy="300" r="50" />
<svg>
</body>
</html>

上面是 SVG 代码直接插入网页的例子。

SVG 代码也可以写在一个独立文件中,然后用<img><object><embed><iframe>等标签插入网页。


<img src="circle.svg">
<object id="object" data="circle.svg" type="image/svg+xml"></object>
<embed id="embed" src="icon.svg" type="image/svg+xml">
<iframe id="iframe" src="icon.svg"></iframe>

CSS 也可以使用 SVG 文件。


.logo {
  background: url(icon.svg);
}

SVG 文件还可以转为 BASE64 编码,然后作为 Data URI 写入网页。


<img src="data:image/svg+xml;base64,[data]">

二、语法

2.1 <svg>标签

SVG 代码都放在顶层标签<svg>之中。下面是一个例子。


<svg width="100%" height="100%">
  <circle id="mycircle" cx="50" cy="50" r="50" />
</svg>

<svg>width属性和height属性,指定了 SVG 图像在 HTML 元素中所占据的宽度和高度。除了相对单位,也可以采用绝对单位(单位:像素)。如果不指定这两个属性,SVG 图像默认大小是300像素(宽) x 150像素(高)。

如果只想展示 SVG 图像的一部分,就要指定viewBox属性。


<svg width="100" height="100" viewBox="50 50 50 50">
  <circle id="mycircle" cx="50" cy="50" r="50" />
</svg>

<viewBox>属性的值有四个数字,分别是左上角的横坐标和纵坐标、视口的宽度和高度。上面代码中,SVG 图像是100像素宽 x 100像素高,viewBox属性指定视口从(50, 50)这个点开始。所以,实际看到的是右下角的四分之一圆。

注意,视口必须适配所在的空间。上面代码中,视口的大小是 50 x 50,由于 SVG 图像的大小是 100 x 100,所以视口会放大去适配 SVG 图像的大小,即放大了四倍。

如果不指定width属性和height属性,只指定viewBox属性,则相当于只给定 SVG 图像的长宽比。这时,SVG 图像的默认大小将等于所在的 HTML 元素的大小。

2.2 <circle>标签

<circle>标签代表圆形。


<svg width="300" height="180">
  <circle cx="30"  cy="50" r="25" />
  <circle cx="90"  cy="50" r="25" class="red" />
  <circle cx="150" cy="50" r="25" class="fancy" />
</svg>

上面的代码定义了三个圆。<circle>标签的cxcyr属性分别为横坐标、纵坐标和半径,单位为像素。坐标都是相对于<svg>画布的左上角原点。

class属性用来指定对应的 CSS 类。


.red {
  fill: red;
}

.fancy {
  fill: none;
  stroke: black;
  stroke-width: 3pt;
}

SVG 的 CSS 属性与网页元素有所不同。

  • fill:填充色
  • stroke:描边色
  • stroke-width:边框宽度

2.3 <line>标签

<line>标签用来绘制直线。


<svg width="300" height="180">
  <line x1="0" y1="0" x2="200" y2="0" style="stroke:rgb(0,0,0);stroke-width:5" />
</svg>

上面代码中,<line>标签的x1属性和y1属性,表示线段起点的横坐标和纵坐标;x2属性和y2属性,表示线段终点的横坐标和纵坐标;style属性表示线段的样式。

2.4 <polyline>标签

<polyline>标签用于绘制一根折线。


<svg width="300" height="180">
  <polyline points="3,3 30,28 3,53" fill="none" stroke="black" />
</svg>

<polyline>points属性指定了每个端点的坐标,横坐标与纵坐标之间与逗号分隔,点与点之间用空格分隔。

2.5 <rect>标签

<rect>标签用于绘制矩形。


<svg width="300" height="180">
  <rect x="0" y="0" height="100" width="200" style="stroke: #70d5dd; fill: #dd524b" />
</svg>

<rect>x属性和y属性,指定了矩形左上角端点的横坐标和纵坐标;width属性和height属性指定了矩形的宽度和高度(单位像素)。

2.6 <ellipse>标签

<ellipse>标签用于绘制椭圆。


<svg width="300" height="180">
  <ellipse cx="60" cy="60" ry="40" rx="20" stroke="black" stroke-width="5" fill="silver"/>
</svg>

<ellipse>cx属性和cy属性,指定了椭圆中心的横坐标和纵坐标(单位像素);rx属性和ry属性,指定了椭圆横向轴和纵向轴的半径(单位像素)。

2.7 <polygon>标签

<polygon>标签用于绘制多边形。


<svg width="300" height="180">
  <polygon fill="green" stroke="orange" stroke-width="1" points="0,0 100,0 100,100 0,100 0,0"/>
</svg>

<polygon>points属性指定了每个端点的坐标,横坐标与纵坐标之间与逗号分隔,点与点之间用空格分隔。

2.8 <path>标签

<path>标签用于制路径。


<svg width="300" height="180">
<path d="
  M 18,3
  L 46,3
  L 46,40
  L 61,40
  L 32,68
  L 3,40
  L 18,40
  Z
"></path>
</svg>

<path>d属性表示绘制顺序,它的值是一个长字符串,每个字母表示一个绘制动作,后面跟着坐标。

  • M:移动到(moveto)
  • L:画直线到(lineto)
  • Z:闭合路径

2.9 <text>标签

<text>标签用于绘制文本。


<svg width="300" height="180">
  <text x="50" y="25">Hello World</text>
</svg>

<text>x属性和y属性,表示文本区块基线(baseline)起点的横坐标和纵坐标。文字的样式可以用classstyle属性指定。

2.10 <use>标签

<use>标签用于复制一个形状。


<svg viewBox="0 0 30 10" xmlns="http://www.w3.org/2000/svg">
  <circle id="myCircle" cx="5" cy="5" r="4"/>

  <use href="#myCircle" x="10" y="0" fill="blue" />
  <use href="#myCircle" x="20" y="0" fill="white" stroke="blue" />
</svg>

<use>href属性指定所要复制的节点,x属性和y属性是<use>左上角的坐标。另外,还可以指定widthheight坐标。

2.11 <g>标签

<g>标签用于将多个形状组成一个组(group),方便复用。


<svg width="300" height="100">
  <g id="myCircle">
    <text x="25" y="20">圆形</text>
    <circle cx="50" cy="50" r="20"/>
  </g>

  <use href="#myCircle" x="100" y="0" fill="blue" />
  <use href="#myCircle" x="200" y="0" fill="white" stroke="blue" />
</svg>

2.12 <defs>标签

<defs>标签用于自定义形状,它内部的代码不会显示,仅供引用。


<svg width="300" height="100">
  <defs>
    <g id="myCircle">
      <text x="25" y="20">圆形</text>
      <circle cx="50" cy="50" r="20"/>
    </g>
  </defs>

  <use href="#myCircle" x="0" y="0" />
  <use href="#myCircle" x="100" y="0" fill="blue" />
  <use href="#myCircle" x="200" y="0" fill="white" stroke="blue" />
</svg>

2.13 <pattern>标签

<pattern>标签用于自定义一个形状,该形状可以被引用来平铺一个区域。


<svg width="500" height="500">
  <defs>
    <pattern id="dots" x="0" y="0" width="100" height="100" patternUnits="userSpaceOnUse">
      <circle fill="#bee9e8" cx="50" cy="50" r="35" />
    </pattern>
  </defs>
  <rect x="0" y="0" width="100%" height="100%" fill="url(#dots)" />
</svg>

上面代码中,<pattern>标签将一个圆形定义为dots模式。patternUnits="userSpaceOnUse"表示<pattern>的宽度和长度是实际的像素值。然后,指定这个模式去填充下面的矩形。

2.14 <image>标签

<image>标签用于插入图片文件。


<svg viewBox="0 0 100 100" width="100" height="100">
  <image xlink:href="path/to/image.jpg"
    width="50%" height="50%"/>
</svg>

上面代码中,<image>xlink:href属性表示图像的来源。

2.15 <animate>标签

<animate>标签用于产生动画效果。


<svg width="500px" height="500px">
  <rect x="0" y="0" width="100" height="100" fill="#feac5e">
    <animate attributeName="x" from="0" to="500" dur="2s" repeatCount="indefinite" />
  </rect>
</svg>

上面代码中,矩形会不断移动,产生动画效果。

<animate>的属性含义如下。

  • attributeName:发生动画效果的属性名。
  • from:单次动画的初始值。
  • to:单次动画的结束值。
  • dur:单次动画的持续时间。
  • repeatCount:动画的循环模式。

可以在多个属性上面定义动画。


<animate attributeName="x" from="0" to="500" dur="2s" repeatCount="indefinite" />
<animate attributeName="width" to="500" dur="2s" repeatCount="indefinite" />

2.16 <animateTransform>标签

<animate>标签对 CSS 的transform属性不起作用,如果需要变形,就要使用<animateTransform>标签。


<svg width="500px" height="500px">
  <rect x="250" y="250" width="50" height="50" fill="#4bc0c8">
    <animateTransform attributeName="transform" type="rotate" begin="0s" dur="10s" from="0 200 200" to="360 400 400" repeatCount="indefinite" />
  </rect>
</svg>

上面代码中,<animateTransform>的效果为旋转(rotate),这时fromto属性值有三个数字,第一个数字是角度值,第二个值和第三个值是旋转中心的坐标。from="0 200 200"表示开始时,角度为0,围绕(200, 200)开始旋转;to="360 400 400"表示结束时,角度为360,围绕(400, 400)旋转。

三、JavaScript 操作

3.1 DOM 操作

如果 SVG 代码直接写在 HTML 网页之中,它就成为网页 DOM 的一部分,可以直接用 DOM 操作。


<svg
  id="mysvg"
  xmlns="http://www.w3.org/2000/svg"
  viewBox="0 0 800 600"
  preserveAspectRatio="xMidYMid meet"
>
  <circle id="mycircle" cx="400" cy="300" r="50" />
<svg>

上面代码插入网页之后,就可以用 CSS 定制样式。


circle {
  stroke-width: 5;
  stroke: #f00;
  fill: #ff0;
}

circle:hover {
  stroke: #090;
  fill: #fff;
}

然后,可以用 JavaScript 代码操作 SVG。


var mycircle = document.getElementById('mycircle');

mycircle.addEventListener('click', function(e) {
  console.log('circle clicked - enlarging');
  mycircle.setAttribute('r', 60);
}, false);

上面代码指定,如果点击图形,就改写circle元素的r属性。

3.2 获取 SVG DOM

使用<object><iframe><embed>标签插入 SVG 文件,可以获取 SVG DOM。


var svgObject = document.getElementById('object').contentDocument;
var svgIframe = document.getElementById('iframe').contentDocument;
var svgEmbed = document.getElementById('embed').getSVGDocument();

注意,如果使用<img>标签插入 SVG 文件,就无法获取 SVG DOM。

3.3 读取 SVG 源码

由于 SVG 文件就是一段 XML 文本,因此可以通过读取 XML 代码的方式,读取 SVG 源码。


<div id="svg-container">
  <svg
    xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    xml:space="preserve" width="500" height="440"
  >
    <!-- svg code -->
  </svg>
</div>

使用XMLSerializer实例的serializeToString()方法,获取 SVG 元素的代码。


var svgString = new XMLSerializer()
  .serializeToString(document.querySelector('svg'));

3.4 SVG 图像转为 Canvas 图像

首先,需要新建一个Image对象,将 SVG 图像指定到该Image对象的src属性。


var img = new Image();
var svg = new Blob([svgString], {type: "image/svg+xml;charset=utf-8"});

var DOMURL = self.URL || self.webkitURL || self;
var url = DOMURL.createObjectURL(svg);

img.src = url;

然后,当图像加载完成后,再将它绘制到<canvas>元素。


img.onload = function () {
  var canvas = document.getElementById('canvas');
  var ctx = canvas.getContext('2d');
  ctx.drawImage(img, 0, 0);
};

四、实例:折线图

下面将一张数据表格画成折线图。


Date |Amount
-----|------
2014-01-01 | $10
2014-02-01 | $20
2014-03-01 | $40
2014-04-01 | $80

上面的图形,可以画成一个坐标系,Date作为横轴,Amount作为纵轴,四行数据画成一个数据点。


<svg width="350" height="160">
  <g class="layer" transform="translate(60,10)">
    <circle r="5" cx="0"   cy="105" />
    <circle r="5" cx="90"  cy="90"  />
    <circle r="5" cx="180" cy="60"  />
    <circle r="5" cx="270" cy="0"   />

    <g class="y axis">
      <line x1="0" y1="0" x2="0" y2="120" />
      <text x="-40" y="105" dy="5">$10</text>
      <text x="-40" y="0"   dy="5">$80</text>
    </g>
    <g class="x axis" transform="translate(0, 120)">
      <line x1="0" y1="0" x2="270" y2="0" />
      <text x="-30"   y="20">January 2014</text>
      <text x="240" y="20">April</text>
    </g>
  </g>
</svg>

五、参考链接

(完)

QCon

腾讯课堂

留言(20条)

出处:

```
<svg>的width属性和height属性,指定了 SVG 图像在 HTML 元素中所占据的宽度和高度。除了相对单位,也可以采用绝对单位(单位:像素)。如果不指定这两个属性,SVG 图像默认占满它所在的 HTML 元素。
```

疑问:

阮老师,如果不指定这两个属性,SVG 图像默认的宽度和高度似乎就是300px、150px,没有占满它所在的HTML元素

测试代码:

>显示的是一个半圆,而没有占满整个div

```
<!DOCTYPE html>
<html>
<head>
<title></title>
<style type="text/css">
.div {
width: 500px;
height: 300px;
}
</style>
</head>
<body>
<div class="div">
<svg>
<circle id="mycircle" cx="150" cy="150" r="150" />
</svg>
</div>
</body>
</html>
```

不知对否

svg 没有设置viewBox属性时,默认宽度是300px , 高度是150px

如果设置了viewBox属性时,默认的宽度和高度则是所在的HTML元素

@郭小帅:

谢谢提醒,应该改过来了,默认大小确实是300像素宽x150像素高。

我看了知乎上的图片还有一些按钮全是svg的,他们的路径是怎么来的呢

为什么章节不全呢??只显示到 3.3 读取... ????

阮老师为何突然开始关注SVG,难道一直不温不火的SVG要火?

@业余草:

谢谢指出,已经改正。

阮老师又出新文章了,SVG 代码还挺多的。

只是,如果能在看到代码的同时也能看到渲染结果的话,我可能会更开心与容易理解。

没效果,感觉这篇博客很难看懂

正想了解什么,阮老师就写什么,哈哈

关于如何缩放 svg 的,css-tricks 有一篇很好的文章: [How to Scale SVG](https://css-tricks.com/scale-svg/)

补点东西吧. 不知有没有人看.

inline SVG 支持资源外链 支持CSS 支持JS

img SVG 不支持资源外链 支持内部CSS 不支持JS

background-img SVG 不支持资源外链 支持内部CSS 不支持JS

background-img BASE64 SVG 不支持资源外链 支持内部CSS 不支持JS

object SVG 支持资源外链 支持内部CSS 支持内部JS

embed SVG 支持资源外链 支持内部CSS 支持内部JS

iframe SVG 支持资源外链 支持内部CSS 支持内部JS

有了这个表, 你会发现几乎在所有情况下. SVG都支持内部CSS. 即在SVG内部写 style 标签定义其自身的样式. (注意: inline SVG 的 style 标签会污染外部 HTML 的 style)

这样的好处是什么?

在 SVG 内部配合CSS媒体查询. 可以实现 Responsive Content 即 内容根据其容器的尺寸自行改变其自身. 我们一直都在讲 Responsive Web design. 但这主要集中在 Page Level. 或者说将 Layout + Content 两者交融在页面被编码时一同考虑响应式代码. 但在实际情况中网站的 Content 会以后续更新的方式被插入进 Page 中的. 其自身若没有 Responsive 能力. 那么将大大影响 Responsive Page 的自由度. 又或者 Content 被使用在不同的页面中的不同容器里. 如果没有 Responsive 能力. 势必大大降低其使用范围. 广告就是典型例子. 同一个广告, 因为其没有 Responsive 能力. 需要为 Responsive Page 的不同状态设计不同尺寸的广告. 又或需要为不同页面中的不同容器设计不同尺寸的广告. 但如果这广告自身有了 Responsive 的能力. 就可以使解决这一问题的难度大大降低. 我近期一直在想要尝试使用 SVG 来解决这一问题.

市面上已经有两三家公司在做 Responsive Content 或称 Responsive Ads 的尝试. 但大多用 iframe 或 JS媒体查询 的 方式来实现. 个人觉得并不优雅. 适应范围也很局限. 要在页面中插入 iframe 及 JS .

我个人认为 SVG 虽然算不上完美方案, 但要明显优于当前方案. 对被插入页面的干扰也将是最小.

代码就不在这里放了. 这层窗户纸捅破应该很多人都能做出来. 如果没有外部资源的引用(如img). 用 img 标签就可以完成这一工作. 如果有外部资源引用且利用js进行引用控制或实现内部交互操作. 用 object 或 embed 标签就能搞定. 个人喜欢 embed 标签. 因为写起来更简单些, 格式也更像是 img 标签.

一直想写一个名为 ReC 的 Responsive Content 编辑器. 以生成 Responsive Content 内容. 但这想法已经快一年了也未实现. 非职业前端. 时间又非常有限. 把想法简单写在这儿吧! 希望可以对大家有所帮助.

P.S. 1. 您漏写了 symbol 标签. 这是 SVG xLink 引用时最重要的标签. 比 defs 好用的多. 因为其可以独立定义 viewBox. 而 viewBox 是 SVG 被使用时最重要的概念之一.

P.S. 2. SVG的代码优化也是门大学问. 毕竟好多SVG是从设计师那边由设计软件生成出来的. 代码优化是必不可少的步骤. 这次回国刚好要给国内的前端同事讲一下这部份. 设计师那边从开始设计SVG时就应有应对方案. 否者再优化也是白搭. 细节不表. 目前看来 illustrator 生成的 SVG 格式要明显好过 Sketch 生成的 SVG . 只是垃圾代码比较多. 可以考虑 SVGO 这个 Tool . 也可以使用 WGUI 版本 SVGOMG .

P.S. 3. SVG 的 JS 框架可选并不多. D3.js 很重大而全, 并且并不单纯针对 SVG . snap.svg 和 svg.js 都不错. 之前学过 snap.svg , 但近期项目发现 svg.js 更好用一些. 还有个 Raphaël.js , 没有玩过, 没有发言权.

好文章 如果加上图就更完美了

早上发的一篇长回复没过审核吗? 白费我写了这么长并首发在这里....

补一条今天刚发现的问题. 公司前端发现一神奇问题: 一些 SVG 文档将 viewbox 写在其内部时, 它被引用到页面中时 viewbox 无效, 但在引用它的 HTML 中写 viewbox 时有效. 开始也被搞晕了. 仔细一看发现 viewbox != viewBox . SVG 是标准 XML , 大小写敏感. 但在 HTML 中大小写不敏感. 大家要注意一下了. 是 viewBox ....

学习阮大神的JavaScript全栈教程,中间react 的一个组件库 recharts 最后生成的就是一个 svg

感觉和Android的Shape XML有点像

和源自于smali语言,因为太复杂已经被废弃,目前有部分浏览器支持

svg的smil动画我以前看教程的时候说是要作废,但其实一直在用,不知道到底是什么情况了

我要发表看法

«-必填

«-必填,不公开

«-我信任你,不会填写广告链接