svg 入门指北

803 阅读11分钟

简介

什么是svg

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

svg的优势

其他图像格式都是基于像素处理的(像素的图像会有很多问题),SVG 则是属于对图像的形状描述,所以它本质上是文本文件,体积较小,且不管放大多少倍都不会失真。
我们可以得到svg几个最大的优势:

  • 可缩放的矢量图——不会失真;
  • svg是由数学组成的,它只是一段段的代码,所以文件的体积会非常小——轻量级;
  • 每个节点都是单独的dom,可以自由绑定事件和操作内容——易于操作和局部更新;

不足之处

  • 不适合非常复杂的内容或者大量节点的场景

基本形状

svg内置了多种形状可以使用

rect 矩形

x、y:距离(0,0)的位置坐标——(0,0)是左上角的点
width、height:宽、高
rx、ry:圆角属性
fill:填充颜色
stroke、stroke-width:边颜色、边宽
opacity:透明度

<svg xmlns="http://www.w3.org/2000/svg" version="2.0">
  <rect x="50" y="20" rx="20" ry="20" width="150" height="150" style="fill:red;stroke:black;stroke-width:5;opacity:0.5" />
</svg>

circle 圆形

cx、cy:圆心的坐标位置;
r:半径

<svg>
   <circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red" />
</svg> 

ellipse 椭圆

上面的椭圆标准方程告诉我们,约定一个椭圆所需要的元素有:长轴半径、短轴半径、点的坐标;
所以我们绘制一个椭圆需要以下要素:
cx、cy:圆心坐标
rx、ry:水平半径、垂直半径

<svg>
  <ellipse cx="240" cy="50" rx="220" ry="30" style="fill:yellow" />
  <ellipse cx="220" cy="50" rx="190" ry="20" style="fill:white" />
</svg>

line 直线

x1、y1:直线开始的坐标点
x2、y2:直线结束的坐标点

<svg>
  <line x1="0" y1="0" x2="200" y2="200"
  style="stroke:rgb(255,0,0);stroke-width:2"/>
</svg>

polygon 多边形

不少于三条边的封闭图形,可以理解为多个点的集合围成的封闭图形

<svg  height="210" width="500">
  <polygon points="200,10 250,190 160,210"
  style="fill:lime;stroke:purple;stroke-width:1"/>
</svg>

polyline 多线段

只有直线的多段形状,也是多个点的集合,不过不会首尾闭合

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <polyline points="20,20 40,25 60,40 80,120 120,140 200,180"
  style="fill:none;stroke:black;stroke-width:3" />
</svg>

path 路径

path是基本形状中最为强大的一个,可以用它来绘制 线条、曲线、弧形多种形状;
path用于绘制路径,path的d属性表示绘制顺序,它是一个长字符串,每个字母表示一个动作,后面跟着坐标;
所以,属性d的值是一个“命令+参数”的序列,每一个命令都用一个关键字母来表示,比如,字母“M”表示的是“Move to”命令,当解析器读到这个命令时,它就知道你是打算移动到某个点。跟在命令字母后面的,是你需要移动到的那个点的x和y轴坐标。比如移动到(10,10)这个点的命令,应该写成“M 10 10”。这一段字符结束后,解析器就会去读下一段命令。每一个命令都有两种表示方式,一种是用大写字母,表示采用绝对定位。另一种是用小写字母,表示采用相对定位(相对于上一个点移动多少距离)。

path指令集概览

指令参数指令说明
Mx y移动到x,y位置 ( move to )
Lx y从当前位置绘制指定线段到 x , y 的位置 ( line to )
Hx从当前位置绘制水平线到 x 位置 ( horizontal line to )
Vy从当前位置绘制垂直线到 x 位置 ( vertical line to )
Cx1 y1 x2 y2 x y从当前位置绘制三次贝塞尔曲线到指定x,y位置:其中 x1, y1 及 x2, y2 为控制点 ( curve )
Sx2 y2 x y从当前点的坐标画条反射的贝塞尔曲线到指定的x,y位置:其中 x2, y2 为反射的控制点 ( smooth curve )
Qx1 y1 x y从当前位置绘制二次贝塞尔曲线到指定x,y位置:其中 x1, y1 为控制点 ( quadratic Bézier curve )
Tx y从当前点的坐标画条反射的二次贝塞尔曲线到指定的x,y位置:以前一个坐标为反射控制点( smooth quadratic Bézier curve )
Arx ry x-axis-rotation large-arc-flag sweep-flag x y从当前点画一条指定的圆弧到目标 x, y 位置:其中 rx, ry 为椭圆形的 x 轴及 y 轴的半径,x-axis-rotation 是弧线与 x 轴的旋转角度,large-arc-flag 配置为 1(最大角度的弧线)或是 0(最小角度的弧线),sweep-flag 配置方向是 1(顺时针方向)或 0(逆时针方向)
Z闭合当前路径 ( closepath )

Path命令总结有如下规律:

  1. 区分大小写:每一个命令都有两种表示方式,一种是用大写字母,表示采用绝对位置。另一种是用小写字母,表示采用相对位置
  2. 最后的参数表示最终要到达的位置
  3. 上一个命令结束的位置就是下一个命令开始的位置
  4. 命令可以重复参数表示重复执行同一条命令
  5. 因为属性d采用的是用户坐标系统,所以不需标明单位

直线命令

首先是“Move to”命令,M,前面已经提到过,它需要两个参数,分别是需要移动到的点的x轴和y轴的坐标。假设,你的画笔当前位于一个点,在使用M命令移动画笔后,只会移动画笔,但不会在两点之间画线。因为M命令仅仅是移动画笔,但不画线。所以M命令经常出现在路径的开始处,用来指明从何处开始画。

M x y

<path d="M0 0" stroke="skyblue"/>
`这里需要注意的是M命令本身并不会进行任何绘画操作,这里是为了表明移动到(0,0)的位置`

能够真正画出线的命令有三个(M命令是移动画笔位置,但是不画线),最常用的是“Line to”命令,L,L需要两个参数,分别是一个点的x轴和y轴坐标,L命令将会在当前位置和新位置(L前面画笔所在的点)之间画一条线段。

L x y

<path d="M0 0 L50 50" stroke="skyblue"/>

另外还有两个简写命令,用来绘制水平线和垂直线。H,绘制水平线。V,绘制垂直线。这两个命令都只带一个参数,标明在x轴或y轴移动到的位置,因为它们都只在坐标轴的一个方向上移动。

V y

<path d="M0 0 V50" stroke="skyblue"/>

H x

<path d="M0 0 H50" stroke="skyblue"/>

Z命令会从当前点画一条直线到路径的起点,即闭合改路径。

<path d="M0 0 Q50 50, 100 0 T200 0 Z" stroke="black" fill="none"/>

曲线命令

三次贝塞尔曲线,最后一个坐标(x,y)表示的是曲线的终点,另外两个坐标是控制点,(x1,y1)是起点的控制点,(x2,y2)是终点的控制点

C x1 y1, x2 y2, x y

<path d="M0 0 C40 40,60 40,100,0" stroke="skyblue" fill="none"/>

此外,可以将若干个贝塞尔曲线连起来,从而创建出一条很长的平滑曲线。通常情况下,一个点某一侧的控制点是它另一侧的控制点的对称(以保持斜率不变)。这样可以使用一个简写的贝塞尔曲线命令S,如下所示:

S x2 y2, x y

<path d="M0 0 C40 40,60 40,100,0 S150 -40, 200 0" stroke="black" fill="none"/>

S命令可以用来创建与前面一样的贝塞尔曲线,但是,如果S命令跟在一个C或S命令后面,则它的第一个控制点会被假设成前一个命令曲线的第二个控制点的中心对称点。如果S命令单独使用,前面没有C或S命令,那当前点将作为第一个控制点。

另一种可用的贝塞尔曲线是二次贝塞尔曲线Q,它比三次贝塞尔曲线简单,只需要一个控制点,用来确定起点和终点的曲线斜率

Q x1 y1, x y

<path d="M0 0 Q50 50, 100 0" stroke="black" fill="none"/>

与三次贝塞尔曲线类似,二次贝塞尔曲线有一个差不多的T命令,可以通过更简短的参数,延长二次贝塞尔曲线

T x y

<path d="M0 0 Q50 50, 100 0 T200 0" stroke="black" fill="none"/>

命令T会通过前一个控制点,推断出一个新的控制点。

弧形

弧形命令A

A rx ry x-axis-rotation large-arc-flag sweep-flag x y
rx : 椭圆的 x 轴半径(根据不同的终点换算成比例)
ry : 椭圆的 y 轴半径(根据不同的终点换算成比例)
x-axis-rotation : 弧线与 x 轴的夹角
large-arc-flag : 1 表示为大角度弧线,0 表示为小角度弧线
sweep-flag : 1 为顺时针,0 为逆时针
x : 终点的 x 坐标
y : 终点的 y 坐标

<path d="M0 0 A50 100,60 0 0 100 0" stroke="#f90" fill="none"/>

上面对于path的介绍还比较为简单,path详情可以参考mdn的解释:
developer.mozilla.org/zh-CN/docs/…

marker 标注

marker可以作为小工具或者标志图标来理解,常见的可以用marker编写箭头

<svg width="" height="200" viewBox="0 0 120 120"
    xmlns="http://www.w3.org/2000/svg" version="1.1">
  <defs>
<marker id="markerCircle" markerwidth="8" markerheight="8" refx="5" refy="5"> 
<circle cx="5" cy="5" r="3" style="stroke: none; fill:#000000;"></circle> 
</marker> 
  </defs>
  <polyline points="10,90 50,80 90,20" fill="none" stroke="black"
      stroke-width="2" marker-end="url(#markerCircle)" />
</svg>

text 文本

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <defs>
  <path id="path1" d="M75,20 a1,1 0 0,0 100,0"/>
  </defs>
  <a xlink:href="https://www.baidu.com" target="_blank">
  <text x="0" y="15" fill="red">I love SVG
  </text>
  </a>
  <text x="10" y="100" style="fill:red;">
    <textPath xlink:href="#path1">this is test</textPath>
    <tspan>span test</tspan>
  </text>
</svg>

text结合textPath、tsapn等可以玩出很多的花样,虽然是简单的文本,但是可以拓展的方向还是很多的

a 超链接

只是标签名和html的a标签一样,但并不是一个元素

<svg width="140" height="30">
  <a xlink:href="https://www.baidu.com"
     target="_blank">
    <rect height="30" width="120" y="0" x="0" rx="15"/>
    <text fill="white" text-anchor="middle"
          y="21" x="60">SVG A TAG</text>
  </a>
</svg>

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>

g 分组

g标签将多个形状分成一个组,方便复用

<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>

defs 标签

defs用于自定义形状,但是与直接使用g不同的是,内部的代码不会显示,仅供通过id引用

<svg width="300" height="100">
  <defs>
    <g id="myCircle">
      <text x="25" y="20">circle</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>

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>

foreignobject

可以包含来自不同的XML命名空间的元素,在浏览器的上下文中,可能是XHTML / HTML

<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
  <style>
    polygon { fill: black }

    div {
      color: white;
      font-size:18px;
      height: 100%;
      overflow: auto;
    }
  </style>

  <polygon points="5,5 195,10 185,185 10,195" />

  <!-- Common use case: embed HTML text into SVG -->
  <foreignObject x="20" y="20" width="160" height="160">
    <!--
      In the context of SVG embeded into HTML, the XHTML namespace could
      be avoided, but it is mandatory in the context of an SVG document
    -->
    <div>
      Lorem ipsum dolor sit amet, consectetur adipiscing elit.
      Sed mollis mollis mi ut ultricies. Nullam magna ipsum,
      porta vel dui convallis, rutrum imperdiet eros. Aliquam
      erat volutpat.
    </div>
  </foreignObject>
</svg>

stroke 属性

stroke:线、文本、元素的轮廓颜色
stroke-width:轮廓的宽度
stroke-linecap:终点的形状属性
stroke-dasharray:定义虚线的属性

animate 动画

animate定义在元素的内部,可以指定元素的属性如何改变,动画也可以配置多个

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

animate.gif

animation 与 滤镜、渐变、模糊、阴影等可以做出很多效果,就不再这展开来讲,可以单独成文

viewport

表示SVG可见区域的大小,或者可以想象成舞台大小,画布大小。

<svg width="500" height="300"></svg>

上面的SVG代码定义了一个视区,宽500单位,高300单位。
width/height如果是纯数字,使用的就是“像素”作为单位的,上面SVG的视区大小就是500px * 300px。

viewBox属性

先看一个svg的例子:

<svg width="400" height="300" viewBox="0,0,40,30" style="border:1px solid #cd0000;">     
  <rect x="10" y="5" width="20" height="15" fill="#cd0000"/> 
</svg>

如果先忽略viewBox,这里svg的尺寸是400*300像素,而的大小只有其1/20,但是显示出来的却明显不止这个大小;
viewBox值有4个数字:

viewBox="x, y, width, height"  // x:左上角横坐标,y:左上角纵坐标,width:宽度,height:高度 

viewBox顾名思意是“视区盒子”的意思,SVG就像是我们的显示器屏幕,viewBox就是截屏工具选中的那个框框,最终的呈现就是把框中的截屏内容再次在显示器中全屏显示;

更直观的解释:

  1. 如果没有viewBox, 应该是长这样的:
  这时候大小只有整个SVG大小的1/20.
  1. viewBox="0,0,40,30"相当于在SVG上圈了下图左上角所示的一个框:
  1. 然后把这个框,连同框里的小矩形一起放大到整个SVG大小:

perserveAspectRatio

上面的例子,SVG的宽高比正好和viewBox的宽高比是一样的,都是4:3. 显然,实际应用viewBox不可能一直跟viewport同等比例。

preserveAspectRatio属性的值为空格分隔的两个值组合而成: xMidYMid和meet.
第1个值表示,viewBox如何与SVG viewport对齐;
第2个值表示,如何维持高宽比(如果有)。
其中,第1个值又是由两部分组成的。前半部分表示x方向对齐,后半部分表示y方向对齐,可选如下:

含义
xMinviewport和viewBox左边对齐
xMidviewport和viewBox x轴中心对齐
xMaxviewport和viewBox右边对齐
YMinviewport和viewBox上边缘对齐。注意Y是大写。
YMidviewport和viewBox y轴中心点对齐。注意Y是大写。
YMaxviewport和viewBox下边缘对齐。注意Y是大写。

x,y可以自由组合

含义
meet保持纵横比缩放viewBox适应viewport
slice保持纵横比同时比例小的方向放大填满viewport
none扭曲纵横比以充分适应viewport

参考资料

MDN:www.yuque.com/u21340737/k…
菜鸟教程:www.runoob.com/svg/svg-tut…
svg详解(张鑫旭):www.zhangxinxu.com/wordpress/2…

常用工具

SVG Path 设计工具:yqnn.github.io/svg-path-ed…