SVG 学习(上)

Sep 08 2018

概念

是什么?

SVG 全称是可缩放矢量图(Scalable Vector Graphics),是一种基于 XML 语法的图形格式。

SVG 早在2003年就成为 W3C 标准,因此各大浏览器到现在对其支持都很好,没有兼容性问题。

兼容性.jpg

因为其图像是使用 XML 标记构建的,通过绘制每个点、线或形等基于数学方程的几何图元绘制图像,而不是用预定义的像素阵列填充空间。所以相比 PNG、JPG 或 Canvas 等栅格图形,SVG 可以任意缩放不失真,方便适配不同尺寸和分辨率的设备。

对比.jpg

另一个很重要点是,由于是在 XML 中定义的,SVG 图像能在浏览器中生成 DOM 节点,我们便可以使用 CSS 和 html 与它们进行交互或控制样式,大大提高了 SVG 的灵活性。

最后,由于 SVG 是用文本描述的,而非二进制,因此可以被记事本等阅读器、搜索引擎访问,提供了在可访问性方面优于传统栅格图像的巨大优势,例如,\<desc> 元素允许你提供图形的详细描述。

长什么样?

SVG 图像使用 XML 定义,因此如果懂 HTML 的话,SVG 看起来会非常熟悉。SVG 代码根元素是一个\<svg>,里面则是各种描述图形信息的元素相互嵌套,元素上有相关的属性。

如下 SVG 图像示例,代表一个 10 x 10 像素的绿色矩形。

<svg width="10" height="10">
  <rect x="0" y="0" width="10" height="10" fill="green" />
</svg>

绿色矩形.png

如果是以 .svg 结尾的 SVG 文件,其头部还会有 XML 声明,用于描述该 SVG 遵循的版本规范,目前常见的版本是1.1。

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

注意:
SVG 的元素和属性必须按标准格式书写,因为 XML 是区分大小写的(这一点和HTML不同)。
SVG 里的属性值必须用引号引起来,就算是数值也必须这样做。

大多情况下,我们并不编写 SVG 代码,而是使用 Sketch 或 illustrator 等工具来创建图像再将其导出为 SVG。那为什么还要学习怎么使用代码来创建 SVG?因为在实际使用过程中有时候需要手动修改导出的代码,来获得我们需要的效果(如去间距,改颜色)。

如何使用?

SVG 文件可以直接内联在 HTML 中,成为 DOM 的一部分。

<!DOCTYPE html>
<html>
<head></head>
<body>
<svg
  width="10"
  height="10"
  version="1.1"
  xmlns="http://www.w3.org/2000/svg"
>
  <rect
    x="0"
    y="0"
    width="10"
    height="10"
    fill="green"
  />
</svg>
</body>
</html>

SVG 代码也可以写在一个独立文件中,通过指向文件地址来嵌入网页。

\<img>、\<object>、\<embed>、\<iframe> 等元素都支持加载 SVG 文件。

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

CSS 属性也可以加载 SVG 文件作为图形,就像使用 PNG, JPG 图片一样。

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

SVG 还可以 Data URI 形式引入。

<img src='data:image/svg+xml;utf8,data:image/svg+xml;utf8,<svg width="10" height="10" version="1.1" xmlns="http://www.w3.org/2000/svg"><rect x="0" y="0" width="10" height="10" fill="green"/></svg>'>
.logo {
  background: url('data:image/svg+xml;utf8,data:image/svg+xml;utf8,<svg width="10" height="10" version="1.1" xmlns="http://www.w3.org/2000/svg"><rect x="0" y="0" width="10" height="10" fill="green"/></svg>');
}

当然还可以以base64 编码再嵌入,但是 SVG 在转 base64 后反而会变大,且 base64 本身解码也会消耗一定的资源,因此不建议使用。

在 Glitch 上可以查看这些示例 flavio-svg-loading-ways.glitch.me/

基础绘图

通过 SVG 元素能够绘制一些基础的形状和线条以及添加文本。每个元素都需要一些属性来指定绘制效果,如坐标和大小等详细信息。

注:以下长度单位若为特殊注明均为像素。

形状

矩形

\<rect> 元素用于绘制矩形。

  <rect x="10" y="10" width="100" height="100" rx="5" ry ="10" />

rect

x 和 y 用来设置矩形左上角端点的横坐标和纵坐标;width 和 height 则用来设置宽度和高度;rx 和 ry 用来设置圆角的横向轴和纵向轴的半径。

圆形

\<circle> 元素用于绘制圆形。

  <circle cx="75" cy="75" r="75" />

circle

cx 和 cy 用来设置圆心的坐标位置;r 用来设置半径的大小。

椭圆形

\<ellipse> 元素用于绘制椭圆形。

  <ellipse cx="100" cy="100" rx="100" ry="50" />

ellipse

cx 和 cy 用来设置圆心的坐标位置;rx 和 ry 用来设置横向轴和纵向轴的半径。

多边形

\<polygon> 元素用于绘制多边形。

  <polygon points="50,5 100,5 125,30 125,80 100,105 50,105 25,80 25,30" />

polygon

points 用来设置每个端点的坐标,横纵坐标之间与逗号分隔,点与点之间用空格分隔。

线条

直线

\<line> 元素用于绘制直线。

  <line x1="5" y1="5" x2="100" y2="100" stroke="black" />

line

x1 和 y1 用来设置线段的起点坐标,x2 和 y2 则用来设置线段的终点坐标。

折线

\<polyline> 元素用于绘制折线。

  <polyline points="50,5 100,5 125,30 125,80 100,105 50,105 25,80 25,30" stroke="black" fill="white" />

polyline

points 用来设置每个端点的坐标,横纵坐标之间与逗号分隔,点与点之间用空格分隔。

文本

\<text> 元素用于绘制文本。

  <text x="30" y="90" font-size="40" font-family="'Leckerli One', cursive">I can eat glass.</text>

text

x 和 y 用来设置文本区块基线(baseline)起点的横坐标和纵坐标;font-size 用来设置字体大小;font-family 用来设置字体类型。

\<text> 不会自动换行,但 <tspan> 允许我们将某些文本单独设置,以绘制多行文本。

  <text x="15" y="90" font-size="60">I can<tspan dy="-30" font-size="80"> eat </tspan> <tspan dy="50">glass.</tspan></text>

tspn

dx 和 dy 用来设置 \<tspan> 内文本相对 \<text> 的 x 和 y 的位置。

style 的 text-anchor 属性可以改变(x,y)作为起始坐标的定义,用于控制文本对齐方式。

  <text x="100" y="30" style="text-anchor: start">Start</text>
  <text x="100" y="60" style="text-anchor: middle">Middle</text>
  <text x="100" y="90" style="text-anchor: end">End</text>

text-anchor

通过 text-length 可以指定文本的长度;配合 lengthAdjust 可以使字母间距和字形大小调整以适应长度。

  <text x="10" y="60" textLength="500" lengthAdjust="spacing">I can eat glass.</text>
  <text x="10" y="100" textLength="500" lengthAdjust="spacingAndGlyphs">I can eat glass.</text>

text-length

letter-spacing 和 word-spacing 可以指定字符或单词之间的间距

  <text x="10" y="30" font-size="40" letter-spacing="30">I can eat glass.</text>
  <text x="10" y="90" font-size="40" word-spacing="30">I can eat glass.</text>

lw

填充与描边

fill 和 stroke 允许我们填充元素内部和绘制其线框,添加颜色、渐变或图案等。

  <rect x="10" y="10" width="100" height="100" fill="blue" stroke="orange" stroke-width="2" />

fs

fill 和 store 还有其他配套属性来调整填充和描边的样式。

  • fill-opacity 设置填充的透明度
  • fill-rule 设置判断点在不在填充范围的算法,当边线交叉时或者内部有“洞”时和有效。值有 inherit、nonzero 和 evenodd(默认值)。

  • stroke-width 设置描边的粗细。

  • stroke-linecap 设置路径端点(也称为线帽)的样式。
  • stroke-linejoin 设置路径连接处的样式。
  • stroke-opacity 设置描边的透明度。
  • stroke-dasharray 设置描边的为虚线并制定虚线的长度及以虚线之间的距离。
  • stroke-dashoffset 指定描边虚线的起始偏移距离。

工作区域

SVG 中最需要掌握的是它的工作区域,换句话说即图形是如何映射到页面中的。理解它有助于帮你更好的,更正确的呈现作品,特别是当进入了高级的 SVG 特性时,这个就变得非常重要。

视口

\<svg> 元素的 width 属性和 height 属性,用来设置 SVG 图形的宽度和高度(默认单位:像素),即 viewport,超出这个区域的图形部分将不显示。如果不指定这两个属性,则 SVG 图形默认大小是 10像素(宽)x 150像素(高)。

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

view1

如果想控制 SVG 图形可见区域,就要指定 viewBox 属性。

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

view2

viewBox 属性的值有四个数字,分别是相对 SVG 内部左上角的横坐标和纵坐标,以及视口的宽度和高度。上面代码中,SVG 图形宽高是100像素,viewBox属性指定视口从(50, 50)这个点开始的宽高为50像素的部分。所以,实际看到的是右下角的四分之一圆。因为视口必须适配所在的空间,所以视口会将该部分图形放大,此处放大了四倍。

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

上面的例子,SVG 的宽高比正好和 viewBox 的宽高比相同,都是1:1。当 viewport 和 viewBox 不具备相同的宽高比例时,preserveAspectRatio 属性将指示浏览器如何显示图形。preserveAspectRatio有两个参数, align 和 meetOrSlice。

<svg width="100" height="100" viewBox="50 50 30 50" preserveAspectRatio="xMidYMid meet">
  <circle cx="50" cy="50" r="50" />
</svg>

view3

preserveAspectRatio 有两个参数,align 表示 viewBox 内对齐基准;meetOrSlice 表示如何维持宽高比例。

align 由两部分组成,前半部分表示 x 方向的对齐,后半部分表示 y 方向的对齐,都有 Min、Mid、Max 三种值,分别为左中右或上中下。如上面例子的 xMidYMid 就代表可视区域以 x 轴和 y 轴为中心对齐,也是默认情况。

meetOrSlice 的值类型如下:

效果
meet(默认情况) 保持比例缩放 viewBox 适应 viewport,部分 viewport 会空白(类似 background-size: contain)
slice 保持比例放大 viewBox 使其覆盖 viewport,部分 viewBox 会被剪切(类似 background-size: cover)
拉伸改变比例以充分适应 viewport,图形可能会扭曲

坐标

理解了 SVG 的视口后,让我们来看看坐标系统。SVG 的坐标系统有三种类型:

  • 用户坐标系(User Coordinate),即画布的坐标系,以 viewport 左上角为原点,用于定位图形。
  • 前驱坐标系(Previous Coordinate),即父容器坐标系,图形进行坐标变换都是基于前驱坐标系。
  • 自身坐标系(Current Coordinate),每个图形或分组都会产生一个自身坐标系,用于定义自己的一些图形属性,例如宽高、位置。
<svg viewBox="0 0 100 100">
  <g transform="translate(20) rotate(30)">
    <rect x="10" y="10" width="20" height="20" />
  </g>
</svg>

view4

如上面的示例,\<svg> 的 viewBox 属性使用用户坐标系,\<rect> 的 rx ry 属性使用自身坐标系,\<g> 则是 \<circle> 的前驱坐标系。

使用 transform 属性可以让坐标系统发生旋转、缩放、位移和扭曲等线性变换,以此改变画布中图形的位置和角度。

transform 属性的值定义了应用于元素及其子元素的变换列表,每个变换由空格或逗号分隔。

常见的 transform 属性的变换类型如下:

变换 描述
none 定义不进行转换。
matrix(n,n,n,n,n,n) 定义 2D 转换,使用六个值的矩阵。
matrix3d(n,n,n,n,n,n,n,n,n,n,n,n,n,n,n,n) 定义 3D 转换,使用 16 个值的 4x4 矩阵。
translate(x,y) 定义 2D 转换。
translate3d(x,y,z) 定义 3D 转换。
translateX(x) 定义转换,只是用 X 轴的值。
translateY(y) 定义转换,只是用 Y 轴的值。
translateZ(z) 定义 3D 转换,只是用 Z 轴的值。
scale(x[,y]?) 定义 2D 缩放转换。
scale3d(x,y,z) 定义 3D 缩放转换。
scaleX(x) 通过设置 X 轴的值来定义缩放转换。
scaleY(y) 通过设置 Y 轴的值来定义缩放转换。
scaleZ(z) 通过设置 Z 轴的值来定义 3D 缩放转换。
rotate(angle) 定义 2D 旋转,在参数中规定角度。
rotate3d(x,y,z,angle) 定义 3D 旋转。
rotateX(angle) 定义沿着 X 轴的 3D 旋转。
rotateY(angle) 定义沿着 Y 轴的 3D 旋转。
rotateZ(angle) 定义沿着 Z 轴的 3D 旋转。
skew(x-angle,y-angle) 定义沿着 X 和 Y 轴的 2D 倾斜转换。
skewX(angle) 定义沿着 X 轴的 2D 倾斜转换。
skewY(angle) 定义沿着 Y 轴的 2D 倾斜转换。
perspective(n) 为 3D 转换元素定义透视视图。

其中 matrix 和 matrix3d 两个矩阵变换分别是实现各种 2D 和 3D 变换的基石。

以 matrix 为例,六个参数代表3*3矩阵上的六个变量,它们与坐标系统的 x 轴和 y 轴相乘后生成新的坐标系统。

matrix

比如一个在原坐标系统(3, 5)的位置在经过变换后就到了(a3+c5+e,b3+d5+f)位置。如果变换为 matrix(7,0,0,8,0,0),则该点变成了(21,40),对照原来在 x 方向放大了7倍,y 方向放大了8倍,相当于 scale(7,8)。

图层

不管是在制图软件中还是 Web 页面的 DOM 元素,都有层级的概念,SVG 当然也不例外。

<svg width="200" height="200">
  <rect width="50" height="50" fill="blue" />
  <ellipse cx="100" cy="100" rx="100" ry="50" fill="green" />
  <circle cx="75" cy="75" r="75" fill="pink" />
  <polygon points="50,5 100,5 125,30 125,80 100,105 50,105 25,80 25,30" fill="gray" />
</svg>

view5

SVG 中元素在 XML 代码中有固定的排列顺序,浏览器渲染时会遵守这个顺序,绘制时也同样会遵守这个顺序。也就是说先出现的元素会出现在绘制的底层,而后出现的元素会绘制在顶层,如果元素间的位置有重叠,则会现后绘制的元素会盖住先出现的元素。

有一点需要特别说的是,SVG 中无法通过类似 z-index 的属性来改变他们层级的,在实际应用中通常都是通过 JavaScript 来动态改变 SVG 的元素顺序以达到改变层级的目的。