简介

简介

传送门

SVG 是 Scalable Vector Graphics 的缩写,意为可缩放矢量图形。于 2003年1月14日 SVG 1.1 被Sun公司(已被Oracle公司收购)、Adobe、苹果公司、IBM 以及柯达共同确立成为W3C推荐标准。

概念

那么SVG究竟是什么呢?

  1. SVG是 Scalable Vector Graphics 的缩写 意为可缩放矢量图形
  2. SVG是一个基于文本的开放网络标准,用来定义用于网络的矢量图形
  3. SVG图像在放大或改变尺寸的情况下其图形质量不会有所损失,因此能够优雅而简洁地渲染不同大小的图形,并和CSS,DOM,JavaScript和SMIL等其他网络标准无缝衔接
  4. SVG 使用 XML 格式定义图形
  5. SVG是万维网联盟的标准与诸如 DOM 和 XSL 之类的 W3C 标准是一个整体

优势

相对于其他格式的图像,SVG的优势有很多:

  1. SVG 与 JPEG 和 GIF 图像比起来,尺寸更小,且可压缩性更强。
  2. SVG图像中的文本是可选的,同时也是可搜索的,且可以与 JavaScript 技术一起运行
  3. SVG可在图像质量不下降的情况下被放大和缩小
  4. SVG可被非常多的工具读取和修改(比如文本编辑器)
  5. SVG图像不依赖分辨率,可在任何的分辨率下被高质量地打印
  6. SVG文件是纯粹的XML
  7. SVG是开放的标准

缺点

  1. SVG复杂度越高渲染速度就会越慢(任何过度使用DOM的应用都不快)
  2. SVG不适合游戏应用,只能结合Canvas来实现
  3. SVG不能动态的修改动画内容

PS: 需要注意的是因为XML和HTML不同,XML是区分大小写的,而SVG是使用XML格式来定义图形,所以在编写SVG的的时候元素和属性必须按标准格式书写。

SVG不是点阵图(用一个个固定的”像素点”拼出来), 而是矢量图.

  • 平常我们要画一个图形, 计算机会记录: 第一个点时黑的, 第五个点是白的
    当我们放大时, 看到的就是一个一个的锯齿方块

但是SVG不记录点, 他只记录一句话: “在中心画一个圆, 半径时5cm”
当我们放大的时候, 浏览器会重新计算这句话, 画出一个更大的, 边缘依然无比光滑的圆

SVG 和 Canvas的区别

  • 绘制方式

    • Canvas:命令式绘图(用 JS 一笔一笔画到像素),画完就“烤死”在画布上。

    • SVG:声明式绘图(写标签描述形状),浏览器负责渲染。

  • 缩放效果

    • Canvas:位图,放大后可能锯齿。

    • SVG:矢量,任意缩放都很清晰。

  • 交互 & 操作

    • Canvas:没有独立图形对象,要自己计算坐标、碰撞等。

    • SVG:每个元素是 DOM,可以直接绑定事件、加动画。

  • 适用场景(一般经验)

    • SVG:图标、Logo、简单/中等复杂度的图表、流程图、地图、矢量插画。

    • Canvas:游戏、海量粒子/点、实时动画、图像处理等高性能需求场景。

浏览器兼容性

这里直接放一张 Can I Use 的详细兼容表。

以上就是SVG的简介,下面将进入SVG的学习。

语法

SVG的绘制其实就是一个SVG标签,然后在标签内绘制你要绘制的内容,比如下面的语法是在SVG标签中绘制了一个圆形(cx、cy为圆的坐标,r为圆的半径)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SVG - 语法</title>
</head>
<body>
<svg>
<circle cx="100" cy="100" r="50"/>
</svg>
</body>
</html>

# 属性

SVG的属性有哪些?

width、height

width、height设置用来设置SVG的宽高。

注意:在不设置宽高的情况下,默认为300 * 150,当内部元素大于300 * 150时,大于部分会被隐藏。

viewBox

viewBox 属性定义了SVG中可以显示的区域。

语法:viewBox="x y w h" x、y为起始点,w、h为显示区域的宽高。

看字面意思,我觉得你肯定是会理解错的,举个例子看一下:

下面我们将在一个尺寸为300 * 300的SVG中绘制一个坐标为(100, 100)半径为100的圆,同时我们加上viewBox属性为”0 0 100 100”

1
2
<svg width="300" height="300" viewBox="0 0 100 100">
<circle cx="100" cy="100" r="100"/>

如图SVG里面的圆只显示了一部份,原因是viewBox定义了一个:从(0, 0)点开始,宽高为100 * 100的显示区域。而这个100 * 100的显示区域会放到300 * 300(svg宽高)的SVG中去显示,整体就*放大了3倍

version

version属性用于指明SVG的版本,也就是指明SVG文档应该遵循的规范。version属性纯粹就是一个说明,对渲染或处理没有任何影响。且目前只有1.0 和 1.1这两个版本。

1
2
3
<svg version="1.1">
// ...
</svg>

上面我们说过SVG使用XML格式定义图形,SVG文件是纯粹的XML文件。

在XML中,标签和属性属于命名空间,这是为了防止来自不同技术的标签和属性发生冲突。例如在SVG中存在a标签,在HTML中也存在a标签,那么怎么区分这个a标签属于哪一种技术,这就需要使用命名空间了。 加入命名空间以后我们就能知道哪一个是svg:a,哪一个又是html:a,这样我们就可以区分出不同的标签和属性

xmlns用于声明命名空间(namespace),在此声明之下的所有子标签都属于这个空间内。这里看起来是一个url,但实际上仅仅是一个字符串,这样使用只是惯例。因此很多时候都会被称为”namespace url” 而不是”namespace name”。

当我们在SVG中加入xmlns时,因为它定义了默认命名空间,因此不需要前缀,我们直接在SVG标签中写一个a标签,a标签和UA就知道它是SVG的a标签而不是HTML的a标签

xmlns: xlink 表示前缀为xlink的标签和属性,应该由理解该规范的UA 使用xlink规范 来解释。

注解:UA是User Agent的简称。User Agent是Http协议中的一部分,属于头域的组成部分。通俗地讲UA是一种向访问网站提供你所使用的浏览器类型、操作系统、浏览器内核等信息的标识。通过这个标识,用户所访问的网站可以显示不同的排版,从而为用户提供更好的体验或者进行信息统计。

1
2
3
  <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
// ......
</svg>

基本图形

基本图形其实就是我们插入到SVG标签中的元素,如上面示例中的圆形(circle)。

下面我们将依次介绍几个基本图形。

圆形(circle)

circle 标签能在屏幕上绘制一个圆形

语法:<circle cx="100" cy="100" r="100"/>

属性:cx、cy为圆的坐标,r为圆的半径

1
2
3
<svg width="300" height="300">
<circle cx="100" cy="100" r="100"/>
</svg>

矩形(rect)

rect标签能在屏幕上绘制一个矩形

语法:<rect x="0" y="0" rx="5" ry="5" width="300" height="200"/>

属性:x、y为矩形的起始点坐标,rx、ry为圆角x、y轴方向的半径, width、height为矩形的宽高

椭圆(ellipse)

ellipse标签比circle标签功能更强大,ellipse标签也可以实现圆形的绘制,并且还可以分别缩放圆形的长轴半径和短轴半径,从而达到椭圆的效果。

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

属性:cx、cy为椭圆的坐标,rx为椭圆的x轴半径、ry为椭圆的y轴半径

线条(line)

line标签可以根据两点的坐标绘制一条直线

语法:<line x1="10" x2="50" y1="110" y2="150"/>

属性:x1、y1为起点的坐标,x2、y2为终点的坐标

1
2
3
4
<svg width="300" height="300">
<!-- 不设置样式属性 style 是看不出效果的 -->
<line x1="50" x2="50" y1="200" y2="50" style="stroke: #000000;"/>
</svg>

折线(polyline)

polyline标签可以把很多个点链接在一起成为一条折线。

语法:<polyline points="0 0, 20 40, 70 80, 100 90, 200 30, 250 50" />

属性:points为点集数列,其中每个点都必须包含2个数字,一个是x坐标,一个是y坐标。

1
2
<!-- 不设置样式属性style是看不出效果的 并且polyline默认为填充需要把fill属性设置为none -->
<polyline points="0 0, 20 40, 70 80, 100 90, 200 30, 250 50" fill="none" style="stroke: #000000;" />

多边形(polygon)

polygon标签和polyline标签类似,都是由很多个点链接在一起的。但不同的是polygon路径中的最后一个点和第一个点是默认闭合的。

语法:<polygon points="0 0, 20 40, 70 80, 100 90, 200 30, 250 50" />

属性:points为点集数列,其中每个点都必须包含2个数字,一个是x坐标,一个是y坐标。

路径(path)

path标签是所有图形中最复杂的,但他也是最强大的。在SVG中最常用的图形就是path标签,他可以绘制圆形、椭圆、矩形、线条、折线、多边形、贝塞尔曲线等。

语法:<path d="M50 50 H 200 V 200 H 50 L 50 50"/>

属性:d为一个点集数列以及其它绘制路径的信息。

路径(path)

上面对路径(path)的举例其实已经是一个很简单的例子了,但是呢我们还是发现我们看不懂,下面我们就看看路径(path)里的这个属性d究竟是什么玩意。

命令

path标签的图形形状是通过属性d来定义的,属性d的值是以:命令 + 参数 的形式进行组合的,命令又是通过关键字来表示的。

那么究竟有哪些命令呢?我总结了一下概括为以下10个命令:

  • M = Move to
  • L = Line to
  • H = Horizontal Line to
  • V = Vertical Line to
  • Q = Quadratic Bezier Curve to
  • T = Smooth Quadratic Bezier Curve to
  • C = Curve to
  • S = Smooth Curve to
  • A = Elliptical Arc
  • Z = close path

下面我们将命令分为直线命令和曲线命令划分开,依次讲解其含义。

直线命令

直线命令的意思就是通过两个点画他们之间的直线。

注意:以下所有命令中 大写为绝对定位 小写为相对定位,后续的命令亦如此

M(Move to)

M命令其实就是把画笔移动到某个点,就好像画笔提起来以后移动到一个新的位置准备开始绘制。但因为仅仅是移动画笔而没有绘制,所以M命令经常出现在路径的起始点,用来指明画笔应该从何处开始绘制。

每一段路径都必须以M命令开头,如果有多个M命令则表示新路径的开始。

语法:M x y 或者 m x y

参数:x、y为坐标

因为M命令只是移动画笔而不画线,所以就先不给出示例,咱们结合下一个命令一起给示例。

L(Line to)

L命令会绘制一点并且和之前的点(也就是L命令前面的点)连成一条直线。

语法:L x y 或者 l x y

参数:x、y为坐标

1
2
3
4
5
6
7
8
9
10
11
<!-- 从起始点(50, 20)画一条到(250, 20)的直线 -->
<path d="M50 20 L250 20" style="stroke: #000000;"/>
<!-- 从起始点(50, 50)画一条到(250, 50)的直线 和 从起始点(50, 100)画一条到(250, 100)的直线 -->
<!-- M命令为多个时,后面的M命令为先线段的起始点 -->
<path d="M50 50 L250 50 M50 100 L250 100" style="stroke: #ff0000;"/>
<!-- 从起始点(50, 150)画一条到(250, 150)的直线 -->
<!-- M命令后面连续跟着多个坐标点,除了第一个坐标点,后面的全部默认为隐式的L命令 -->
<path d="M50 150 250 150" style="stroke: #00ff00;"/>
<!-- 从起始点(50, 200)画一条到(250, 200)又到(250,250)的折线 -->
<!-- 多个L命令连续可以省略后面的L命令 -->
<path d="M50 200 L250 200 250 250 " fill="none" style="stroke: #0000ff;"/>

H(Horizontal Line to)

H命令可以从之前的点绘制一条水平的直线,H命令可以等价于y值和之前点相同的L命令

语法:H x 或者 h x

参数:x为X轴坐标

V(Vertical Line to)

V命令可以从之前的点绘制一条垂直的直线,V命令可以等价于x值和之前点相同的L命令

语法:V y 或者 v y

参数:y为Y轴坐标

1
2
<!-- 从起始点(50, 20)画一条Y轴为250的垂直直线 -->
<path d="M50 20 V250" style="stroke: #000000;"/>

Z(Close path)

Z命令是一个闭合命令,他会从当前点画一条直线到路径的起始点。
Z命令因为没有参数所以Z和z效果一样,所以不区分大小写,

语法:Z 或者 z

参数:无

曲线命令

曲线命令顾名思义就是绘制平滑的曲线

Q(Quadratic Bezier Curve to)

Q命令可以用来绘制一条二次贝塞尔曲线,二次贝塞尔曲线需要一个控制点,用来确定起点和终点的曲线斜率。

语法:Q x1 y1, x y 或者 q x1 y1, x y

参数:x、y为终点位置,x1、y1为控制点
有没有到发现,参数中只有终点没有起点?其实起点就是M命令

1
<path d="M50 100 Q 175 200 300 100" fill="none" style="stroke: #ff0000;"/>

T(Smooth Quadratic Bezier Curve to)

T命令时一个延长二次贝塞尔曲线的简化命令,T命令可以通过前一个控制点推断出后一个控制点,这也就是为什么T命令只需要一个坐标的原因。

需要注意的是T命令的的前面必须有一个Q命令或者其他的T命令。如果T命令单独使用,那么控制点就会被认为和终点是同一个点,所以画出来的将是一条直线

语法:T x y 或者 t x y

参数:x、y为终点位置。

1
<path d="M50 30 Q 100 100 230 30 T 500 300" style="stroke: orange; fill: none"/>

C(Curve to)

C命令可用来绘制一条三次贝塞尔曲线,相对于二次贝塞尔曲线多了一个控制点。

语法:C x1 y1, x2 y2, x y 或者 c x1 y1, x2 y2, x y

参数:x、y为终点位置,x1、y1为曲线起始点的控制点,x2、y2为曲线终止的控制点。

1
<path d="M50 200 C 100 250, 200 150, 250 200" fill="none" style="stroke: #ff0000;"/>

填充和轮廓

在上面的例子中我们经常用这样的属性:

1
fill="none" style="stroke: #ff0000"

fill

fill属性用于填充图形的颜色

语法:fill= "colour" 或者 style="fill: colour"

在不设置fill属性的时候,其实默认是黑色的填充,这也就是为什么上面的很多例子我们设置了 fill='none' 其实就是去掉默认填充。

fill属性其实还有一些延伸属性:

fill-opacity

fill-opacity属性用于设置填充颜色的透明度

1
<circle cx="190" cy="200" r="100" fill="skyblue" fill-opacity="0.2"></circle>

fill-rule

fill-rule属性用来设置复杂形状的填充规则。它有两种填充方式:nonzero 和 evenodd。 该属性简单说就是判断某点属于该形状的“内部”还是“外部”。那么判断的规则是什么呢?

nonzero

nonzero为默认值,规则为:要判断一个点是否在图形内,从该点作任意方向的一条射线,然后检测射线与图形路径的交点情况。从0开始计数,路径从左向右(顺时针)穿过射线则计数加1,从右向左(逆时针)穿过射线则计数减1。得出计数结果后,如果结果是0,则认为点在图形外部,否则认为在内部。

是不是感觉还是有点懵?举个例子看一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SVG - fill</title>
</head>
<body>
<svg width="12cm" height="4cm" viewBox="0 0 1200 400">
<defs>
<path id="Triangle" d="M 16,0 L -8,9 v-18 z" fill="#66ff66" stroke="none" />
</defs>
<g fill-rule="nonzero" fill="#6666ff" stroke="#ff6666" stroke-width="3" >
<path d="M 250,75 L 323,301 131,161 369,161 177,301 z" />
<use xlink:href="#Triangle" transform="translate(306.21 249) rotate(72)" />
<use xlink:href="#Triangle" transform="translate(175.16,193.2) rotate(216)" />
<use xlink:href="#Triangle" transform="translate(314.26,161) rotate(0)" />
<use xlink:href="#Triangle" transform="translate(221.16,268.8) rotate(144)" />
<use xlink:href="#Triangle" transform="translate(233.21,126.98) rotate(288)" />
<path d="M 600,81 A 107,107 0 0,1 600,295 A 107,107 0 0,1 600,81 z
M 600,139 A 49,49 0 0,1 600,237 A 49,49 0 0,1 600,139 z" />
<use xlink:href="#Triangle" transform="translate(600,188) rotate(0) translate(107,0) rotate(90)" overflow="visible" />
<use xlink:href="#Triangle" transform="translate(600,188) rotate(120) translate(107,0) rotate(90)" overflow="visible" />
<use xlink:href="#Triangle" transform="translate(600,188) rotate(240) translate(107,0) rotate(90)" overflow="visible" />
<use xlink:href="#Triangle" transform="translate(600,188) rotate(60) translate(49,0) rotate(90)" overflow="visible" />
<use xlink:href="#Triangle" transform="translate(600,188) rotate(180) translate(49,0) rotate(90)" overflow="visible" />
<use xlink:href="#Triangle" transform="translate(600,188) rotate(300) translate(49,0) rotate(90)" overflow="visible" />
<path d="M 950,81 A 107,107 0 0,1 950,295 A 107,107 0 0,1 950,81 z
M 950,139 A 49,49 0 0,0 950,237 A 49,49 0 0,0 950,139 z" />
<use xlink:href="#Triangle" transform="translate(950,188) rotate(0) translate(107,0) rotate(90)" overflow="visible" />
<use xlink:href="#Triangle" transform="translate(950,188) rotate(120) translate(107,0) rotate(90)" overflow="visible" />
<use xlink:href="#Triangle" transform="translate(950,188) rotate(240) translate(107,0) rotate(90)" overflow="visible" />
<use xlink:href="#Triangle" transform="translate(950,188) rotate(60) translate(49,0) rotate(-90)" overflow="visible" />
<use xlink:href="#Triangle" transform="translate(950,188) rotate(180) translate(49,0) rotate(-90)" overflow="visible" />
<use xlink:href="#Triangle" transform="translate(950,188) rotate(300) translate(49,0) rotate(-90)" overflow="visible" />
</g>
</svg>
</body>
</html>

PS:上面示例中的绿色三角形只是用来辅助理解的,可以忽略,咱们只需要了解规则是如何填充的就行。

效果图:

evenodd

规则为:要判断一个点是否在图形内,从该点作任意方向的一条射线,然后检测射线与图形路径的交点的数量。如果结果是奇数则认为点在内部,是偶数则认为点在外部。

1
<g fill-rule="evenodd" fill="#6666ff" stroke="#ff6666" stroke-width="3" ></g>
## stroke

stroke属性用来定义线条、文本或元素轮廓的颜色。
语法:stroke="colour" 或者 style="stroke: colour"

stroke-width

stroke-width属性定义了轮廓的宽度

1
<circle cx="200" cy="200" r="50" stroke="#ff6666" stroke-width="10"/>

stroke-opacity

stroke-opacity属性用于设置轮廓的透明度

1
stroke-opacity="0.5"

stroke-linecap

stroke-linecap属性定义了轮廓终点的形状,该属性有三个值:

  • butt:默认值,以直边结束线段
  • round:以圆角结束线段,圆角的半径由stroke-width(轮廓宽度)控制的
  • square:也是以直边结束线段,但和butt不同的是会在结束位置多出一段由stroke-width(轮廓宽度)大小控制的长度。
1
stroke-linecap="round"

stroke-linejoin

stroke-linejoin属性定义了轮廓连接处的样式。样式有三种类型:

  • miter:默认值,表示用方形画笔在连接处形成尖角
  • round:用圆角连接,实现平滑效果
  • bevel:连接处会形成一个斜面

stroke-dasharray

stroke-dasharray属性可以定义轮廓为虚线

语法:stroke-dasharray="xxx"

参数:xxx为一列数字字符串,对应的是:线段 空格 线段 空格……

1
2
3
4
5
6
7
8
<svg width="300" height="300">
<g fill="#ffff00" stroke="#ff0000" stroke-width="5">
<path d="M20 50 l200 0" />
<path stroke-dasharray="5, 10" d="M20 100 l200 0" />
<path stroke-dasharray="5, 10, 5" d="M20 150 l200 0" />
<path stroke-dasharray="10, 5, 20" d="M20 200 l200 0" />
</g>
</svg>

stroke-dashoffset

stroke-dashoffset 属性用于指定路径开始的距离。值可为正值、负值、百分比。

stroke-dashoffset

stroke-dashoffset 属性用于指定路径开始的距离。值可为正值、负值、百分比。

1
2
3
4
5
6
7
8
<svg width="300" height="300">
<g fill="#ffff00" stroke="#ff0000" stroke-width="5" stroke-dasharray="20">
<path d="M50 50 l200 0" />
<path d="M50 100 l200 0" stroke-dashoffset="10" />
<path d="M50 150 l200 0" stroke-dashoffset="1%" />
<path d="M50 200 l200 0" stroke-dashoffset="-10" />
</g>
</svg>

stroke-miterlimit

如果两条线交汇在一起形成一个尖角,而且属性 stroke-linejoin 指定了 miter,斜接有可能扩展到远远超过路径轮廓线的线宽。属性 stroke-miterlimit 对斜接长度和stroke-width的比率强加了一个极限。当极限到达时,交汇处由斜接变成倒角。

文字

SVG中我们不仅可以绘制各种图形,我们还可也以绘制文字。

text

通过text标签我们可以在SVG中添加文字,

1
<text x="50" y="50">Hello SVG!</text>

从上面的实例我们可以看到,text标签有一些自己的属性,下面我们细说一下text标签的属性。

x和y

x和y属性决定了文字的绘制起点。如上面的例子我们就是从坐标(50,50)的位置开始绘制的文字。

但需要注意的是x和y的值可以是一个数列。如果设置为了一个数列则会应用到每一个字符上

1
2
3
4
5
6
7
8
9
<text 
x="30 60 90 120 150 180 210 240 270"
y="60 90 120 150 180 150 120 90 60"
fill="#f00"
stroke="#0f0"
font-size="50"
font-weight="bold" >
Hello Svg !
</text>
### dx和dy

dx和dy属性与x和y属性不同的是,x和y属性是绝对的坐标,而dx和dy属性是相对于当前位置的偏移量。

参数也可以是一个数列。如果设置为了一个数列则会应用到每一个字符上

1
2
3
4
5
6
7
8
9
<text 
dx="50 10 10 10 10 10 10 10 10"
dy="50 20 -20 20 -20 20 -20 20 -20"
fill="#f00"
stroke="#0f0"
font-size="50"
font-weight="bold" >
Hello Svg !
</text>

![[98238c1d2d3ff989991c0dd6d9c739c9_MD5.webp]]

rotate

rotate属性可以把文字旋转一个角度。

1
<text rotate="10"> </text>

同样的参数也可以是一个数列。如果设置为了一个数列则会应用到每一个字符上

textLength

textLength属性给定了一个字符串的计算长度。在文字的长度和textLength属性给定的长度不一致的情况下渲染引擎会精细调整字型的位置。

1
2
3
<text  x="50" y="100" textLength="500" fill="#f00" stroke="#0f0" font-size="50" font-weight="bold" >
Hello Svg !
</text>

lengthAdjust

lengthadjust属性可以控制文本以什么方式伸展到由_textLength_属性定义的长度。

  • spacing:只拉伸或压缩间距(文字不变形)
  • spacingAndGlyphs:同时拉伸或压缩间距和文字本身(文字变形)

fill和stroke

填充和轮廓也都可以应用于文字

1
<text x="50" y="50" fill="#f00" stroke="#0f0" font-weight="bold">Hello Svg !</text>

CSS文字属性

一些CSS中的文字样式属性同样也可以应用于SVG的文字中。比如上面例子中我们用到的文字加粗:font-weight 还有很多属性同样也适用,例如:font-size、font-family、font-style、font-variant、font-stretch、font-size-adjust、kerning、letter-spacing、word-spacing、text-decoration等。

1
<text x="50" y="50" fill="#f00" stroke="#0f0" font-weight="bold" font-size="50" font-family="arial">Hello Svg !</text>

tspan

tspan标签和text标签一样都可以用来添加文字,但不同的是tspan标签的作用为标记大块文本内的部分内容。比如一段文本其中某个字需要加粗或者颜色不一致,就可以用到tspan标签。

tspan标签的属性和text标签一致,上面说道的text标签的属性在tspan标签中也适用。

需注意的是tspan标签必须是一个text元素的子元素或别的子元素tspan的子元素

1
2
3
<text x="50" y="50" fill="#f00" stroke="#0f0" font-size="50" >
Hello <tspan fill="#f0f" font-weight="bold"> 小 </tspan> Svg !
</text>

需要注意的是tspan标签的x、y和 dx、dy 会对标签后面的内容造成影响

1
2
3
<text x="50" y="50" fill="#f00" stroke="#0f0" font-size="50" >
Hello <tspan x="150" dy="70" fill="#f0f" font-weight="bold"> 小 </tspan> Svg !
</text>

tref

tref标签配合xlink:href属性能引用已经定义的文本,实现复制的效果。并且引用之后还可以单独定义他的样式。

但遗憾的是 tref标签是SVG 1.1规范的内容,它已经从SVG 2.0规范中删除了。

因此此处咱们可以使用 use标签来代替它,这里说明一下:

  • defs标签可以定义一些之后绘制中需要重复使用的图形元素,defs是definitions的缩写 。
  • use标签可以在SVG文档内读取目标节点,并在别的地方复制使用。
1
2
3
4
5
6
7
8
9
<svg width="500" height="500">
<defs>
<text x="50" y="50" id="text" stroke="#000" font-size="30" >今天天气不错!</text>
</defs>
<!-- tref 已废弃 -->
<!-- <tref x="50" y="100" fill="#f00" xlink:href="#text" /> -->
<use x="50" y="100" fill="#f00" xlink:href="#text" />
<use x="50" y="150" fill="#0f0" rotate="45" xlink:href="#text" />
</svg>

![[2806024d6fb811d0ec7eb588a5024cf6_MD5.webp]]

textPath

textPath标签可以利用它的xlink:href属性取得一个任意路径,并且可以让字符顺着路径渲染。

示例:

1
2
3
4
5
6
7
8
9
10
<svg width="600" height="500">
<path id="pathM" d="M 50 50 100 100 200 50 300 100" fill="none" />
<path id="pathQ" d="M50 100 Q 175 200 300 100 T 600 100" fill="none" />
<text>
<textPath xlink:href="#pathM"> Welcome to the world of SVG ! </textPath>
</text>
<text>
<textPath xlink:href="#pathQ"> Welcome to the world of SVG ! Welcome to the world of SVG ! </textPath>
</text>
</svg>

![[77fa2404069e8a96e205da020fd9d957_MD5.webp]]

渐变

了解渐变的小伙伴都知道,渐变就是从一个颜色过渡到另一个颜色,且渐变都分为两种渐变:线性渐变和径向渐变
在SVG中渐变依旧如此。
但需要注意的是这里我们需要接触两个新的标签:

  • defs标签用来定义渐变
  • stop标签用来定义渐变的颜色坡度,具有三个属性:offset定义渐变开始和结束的位置、stop-color(定义颜色)和stop-opacity(定义透明度)

线性渐变

线性渐变(linearGradient)就是沿直线改变颜色。

语法:

1
2
3
4
5
6
7
<linearGradient x1="" y1="" x2="" y2="">
<stop offset="0%"/>
...
<stop offset="20%"/>
...
<stop offset="100%"/>
</linearGradient>

参数:x1、y1定义线性渐变的起点, x2、y2定义渐变的终点。

径向渐变

径向渐变(radialGradient)其实就是以一个点做放射性的渐变。

1
2
3
4
5
6
7
8
<radialGradient cx="" cy="" r="" fx="" fy="">
<stop offset="0%"stop-color="rgb(255, 255, 0)"/>
...
<stop offset="20%" stop-color="rgb(255, 50, 0)"/>
...
<stop offset="100%" stop-color="rgb(255, 0, 0)"/>
</radialGradient>

参数: cx、cy、r分别为圆的坐标和半径,也就是渐变的范围,fx、fy定义渐变的中心点,也叫渐变的焦点。
不管是线性渐变还是径向渐变,他们不仅可以用于图形还是用于文字

裁剪和蒙层

裁剪和蒙层是我们在之后的SVG应用中常用的功能之一。总而言之是一些特定场景必不可少的功能。

裁剪

裁剪的功能主要是使用clipPath标签定义一条裁剪路径,然后用来裁剪掉元素的部分内容。且任何透明度的效果都是无效的,它只能要么裁剪掉要么不裁剪。

1
2
3
4
5
6
7
8
9
10
<svg width="300" height="300">
<defs>
<clipPath id="clipPath">
<path d="M10 50 A50 50 0 0 1 100 50 A50 50 0 0 1 190 50 Q210 100 100 200 Q-5 100 10 50 Z" />
</clipPath>
</defs>

<rect x="0" y="0" width="200" height="200" fill="#f00" clip-path="url(#clipPath)" />
</svg>

蒙层

蒙层的功能主要实现标签就是mask标签,他的功能和名字正好相反,他不是用来遮住元素的部分内容,而是用来显示元素中mask标签遮住的内容。 他和clipPath标签不同的是他允许使用透明度(透明度为0则无蒙层效果)和灰度值遮罩计算得的软边缘

示例:

1
2
3
4
5
6
7
8
9
<svg width="300" height="300">
<defs>
<mask id="Mask">
<path d="M10 50 A50 50 0 0 1 100 50 A50 50 0 0 1 190 50 Q210 100 100 200 Q-5 100 10 50 Z" fill="#fff" fill-opacity="0.5" />
</mask>
</defs>

<rect x="0" y="0" width="200" height="200" fill="#f00" mask="url(#Mask)" />
</svg>

动画

基础动画

在做动画之前呢咱们需要先了解一下动画的基础知识。咱们再学习CSS的时候其实都有学过,大概就是平移、缩放、旋转等一些transform属性的一些方法。下面我们依次说一下,就算是做动画之前的一个小复习吧。

translate(平移)

translate()方法主要的功能就是用来平移的,把元素从一个地点平移到另一个地点。

语法:transform="translate(x, y)"

参数:x为X轴上的平移距离,y为Y轴上的平移距离,

1
<rect x="0" y="0" width="100" height="200" fill="orange" transform="translate(100, 100)"></rect>

scale(缩放)

scale()方法可以用来缩放元素,通过缩放来控制元素的大小。

语法:transform="scale(x, y)"

参数:x为X轴上的缩放大小,y为Y轴上的缩放大小,当两个值一样时,写一个值就可以。

rotate(旋转)

rotate()方法可以用来做元素的旋转功能,把元素从一个地点平移到另一个地点。

语法:transform="rotate(deg)"

参数:deg为旋转的角度,45度就写45度。

元素的旋转中心点是(0, 0)。如果我们想要只是选中而不位移,那么就需要把旋转的中心点设置在元素的中心点

以上面的例子为例,元素的x坐标为200,本身宽度为100,则x轴元素的中心点就为250,y轴元素的坐标为0,高度为100,则y轴元素的中心点为50。因此元素的中心点就为(250, 50)。

transform-origin

上面我们已经得到了元素的中心点,那么如何设置元素的中心点呢?

我们需要通过transform-origin属性来设置元素的中心点。直接看例子:

1
<rect x="200" y="100" width="100" height="200" fill="orange" transform-origin="200 200" transform="rotate(45)"></rect>

skew(倾斜)

skew()方法可用来实现元素的倾斜,可根据参数去分别做X轴和Y轴的倾斜度。

语法:transform="skewX(x) skewY(y)"

参数:x为X轴上的倾斜度,y为Y轴上的倾斜度。

在SVG中skew属性需要分开设置,x轴设置为skewX,y轴设置为skewY,不能合并起来用,写成 skew(x, y) 是不生效的。

JS动画

在文章最开始的时候我们就说过:SVG不能动态的修改动画内容。

所以CSS3的过渡属性就不好使了,那么想实现动画就只能使用js的定时器(setInterval)来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 <svg width="500" height="500">
<rect id="svg" x="0" y="0" width="100" height="100" fill="#ff770f"/>
</svg>

<script>
const svgEl = document.getElementById('svg')
let x=0, y=0, add=true
setInterval(()=>{
x+=1
if(y<150 && add){
y+=1
} else {
add=false
y-=1
}
console.log(`x: ${x}, y: ${y}`)
svgEl.setAttribute('transform', `translate(${x}, ${y})`)
}, 10)
</script>

![[a08a03635dd1f949b99db3c1463964e8_MD5.webp]]

SVG最拿得出手的线条动画又如何用js来实现呢?比如我们需要变换图形:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<svg width="500" height="500" fill="orange">
<path id="svgPath" />
</svg>

<script>
const svgPath = document.getElementById('svgPath')
let x=250, x1=250, y=100
setInterval(()=>{
if(x<350) {
x+=0.1
} else {
x1=300
if(y<200)y+=0.1
}
svgPath.setAttribute('d', `M 250 100 300 0 ${x} 100 ${x1} ${y} z`)
}, 0)
</script>

![[48acb646e0e12efb50546907d9dee351_MD5.webp]]

是不是越来越有SVG的样了?

但不幸的是,用js操作真的是很费劲。加入我们这个不是直线变三角再变菱形,而是正方形变十边形再变圆形最后变五角星。你想想这个计算是不是很头大?那有没有什么简单的方法呢?

这个问题我们先留着,接下来我们看看CSS3配合SVG做动画会怎么。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
  <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SVG - 动画</title>
<style>
body {
background: #000000;
margin-top: 100px;
}

.svg-wrapper {
height: 60px;
margin: 0 auto;
position: relative;
top: 50%;
transform: translateY(-50%);
width: 320px;
}

.rect {
fill: transparent;
stroke-dasharray: 200 540;
stroke-dashoffset: -445;
stroke-width: 8px;
}
@keyframes draw {
0% {
stroke-dasharray: 200 540;
stroke-dashoffset: -445;
stroke-width: 8px;
}
100% {
stroke-dasharray: 760;
stroke-dashoffset: 0;
stroke-width: 2px;
}
}

.svg-wrapper:hover .rect {
-webkit-animation: 0.5s draw linear forwards;
animation: 0.5s draw linear forwards;
}
</style>
</head>
<body>

<div class="svg-wrapper">
<svg height="60" width="320" xmlns="http://www.w3.org/2000/svg">
<text x="80" y="40" class="text" font-size="30" fill="#fff">Hello SVG</text>
<rect class="rect" height="60" width="320" stroke="#fff" />
</svg>
</div>

</svg>

</body>
</html>

![[8526ef464eb0d01716ed028e7d3b26bc_MD5.webp]]

解释:

  • stroke-dasharray: 定义虚线的长度和空白, 这里只留下200的实现, 其他全为空
  • stroke-dashoffset: -445: 将线移到下边来
  • @keyframes draw 由现阶段慢慢向最终阶段变化靠拢

GreenSock

GreenSock 动画平台 (GSAP) 是一个业界知名的工具套件,用于 超过 1100 万个网站,其中包括超过 50% 的 获奖网站 ! 您可以使用 GSAP 在任何框架中为 JavaScript 可以触及的几乎所有内容制作动画。 无论您是想要为 UI、SVG、Three.js 还是 React 组件制作动画,GSAP 都能满足您的需求。核心库 包含创建超快速、跨浏览器友好动画所需的一切。这就是我们将在本文中逐步介绍的内容。

除了核心,还有各种插件。我们无需学习它们即可开始,但它们可以帮助解决特定的动画挑战,例如 基于滚动的动画、 可拖动 交互、变形等。

引入

1
2
// 引入 CND 地址
<script src="https://cdn.jsdelivr.net/npm/gsap"></script>

补间类型

下面介绍一下GSAP的使用和常用方法。

先举个例子看一下。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SVG - 动画</title>
<script src="https://cdn.jsdelivr.net/npm/gsap"></script>
</head>
<body>

<div id="svg-wrapper">
<svg width="500" height="500" xmlns="http://www.w3.org/2000/svg">
<rect id="rect" width="50" height="50" fill="orange"></rect>
</svg>
</div>
<script>
gsap.to('#rect', {
x: 300, // transform: translateX(300px)
duration: 2 // 动画执行时间2S
})
</script>
</svg>
</body>
</html>

![[3456a1a4d3a5518afc414895fb7960f5_MD5.webp]]
如上面的例子可以看出,我们只需要一个 .to() 方法就可以实现我们想要的动画。而像 .to()方法这样的补间类型还有三个:

补间有四种类型:

  • gsap.to():这是最常见的补间类型。 .to()补间将从元素的当前状态开始 “到” 补间中定义的值。
  • gsap.from():和 .to() 正好相反,.from()补间中定义的值 为动画开始的状态。
  • gsap.fromTo():定义了起始值和结束值。
  • gsap.set():立即设置属性(无动画)。

接下来我们将应用这几个补间类型实现一些酷炫点的动画。

案例1:下拉松开以后的回弹动效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SVG - 动画</title>
<script src="https://cdn.jsdelivr.net/npm/gsap"></script>
</head>
<body>

<div id="svg-wrapper">
<svg width="500" height="500" xmlns="http://www.w3.org/2000/svg">
<circle id="circle" cx="100" cy="50" r="50" fill="orange" />
</svg>
</div>
<script>
gsap.to('#circle', {
y: 400, // transform: translateY(400px)
duration: 2, // 动画执行时间2S
ease: "bounce.out", // 动画轨迹
})
</script>
</svg>
</body>
</html>

![[41e2b099a8d7d9dab8b7bb8f0c6516c5_MD5.webp]]

复杂的样式光靠代码是写不出来的, 我们还需要借助各种工具辅助画图生成相关代码, 而不是手搓