我们为什么使用svg
SVG的优缺点
SVG(Scalable Vector Graphics)是一种基于XML的矢量图形格式,用于在网页和应用程序中显示图形。
优点:
- 矢量图,无惧放大(放大不会模糊)
- 存储占用小(通常比 jpeg、png 等传统图片占用存储空间小)
- 通过XML定义,可访问性好(如屏幕阅读器识别),可通过 CSS 和 Javascript 与其交互,也可通过 GZip 对齐进行压缩
- 支持在线编辑(如 https://editor.method.ac/),也可通过各种制图软件(如**Illustrator**)绘制
缺点:
- 复杂图形可能影响性能(渲染时需要大量计算)
- 旧版本浏览器兼容性较差
- 有一定的学习成本
- 不适用于复杂的图形
综上所述,SVG适用于图标等简单图形 以及 需要在不同设备上高质量的图形(无惧放大)。然而,对于复杂的图像或照片,传统的位图格式(如 JPEG 和 PNG)可能更适合。
简单用法
https://developer.mozilla.org/zh-CN/docs/Web/SVG/Tutorial
1 |
|
高级用法
SVG文档声明
1
2
3
4
<svg width="300" height="300" viewBox="0, 0, 100, 200" xmlns="http://www.w3.org/2000/svg" version="1.1">
</svg>第一行
<?xml version="1.0" standalone="no"?>
是 SVG 的文档声明方式,和 HTML 文档的 DTD 声明(<!DOCTYPE html>
)是类似的。一般如果 SVG 运用在 HTML 里,我们可以不写这样的文档声明,但如果是单独的 SVG 文件,那就需要写了,否则浏览器可能会不认识。standalone 属性是在表明该 xml 声明是否是独立的,这里不独立因此后面 DOCTYPE 引入了外部的dtd
version 属性用于指明 SVG 文档遵循规范的版本,只有1.0 和 1.1.这两个有效的选择。
第三行 xmlns 属性是 SVG 的 XML 声明空间,这一部分类似于 HTML 中的
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
画布大小与视口范围
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
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>svg { border: 1px solid black; }</style>
</head>
<body>
<h1> SVG 画布与视图 </h1>
<!-- width,height指定画布大小,viewBox不指定默认是画布大小 -->
<svg width="100" height="100" >
<circle cx="50" cy="50" r="49" stroke="black" stroke-width="1" fill="red" />
</svg>
<!-- 当画布变大,图形则无法自动缩放适应 -->
<svg width="200" height="200" >
<circle cx="50" cy="50" r="49" stroke="black" stroke-width="1" fill="red" />
</svg>
<!-- 通过指定 viewBox 则可自适应画布大小,更多见:https://developer.mozilla.org/zh-CN/docs/Web/SVG/Attribute/viewBox -->
<svg width="200" height="200" viewBox="0, 0, 100, 100">
<circle cx="50" cy="50" r="49" stroke="black" stroke-width="1" fill="red" />
</svg>
<!-- 当画布大小与 viewBox 宽高比不一致时,图形则水平垂直居中放置 -->
<svg width="100" height="200" viewBox="0, 0, 100, 100">
<circle cx="50" cy="50" r="49" stroke="black" stroke-width="1" fill="red" />
</svg>
<!-- 通过指定 preserveAspectRatio 则可指定图形的位置,更多见:https://developer.mozilla.org/zh-CN/docs/Web/SVG/Attribute/preserveAspectRatio
1. preserveAspectRatio 用于当 viewport 和 viewBox 宽高比不相同时,指定这个坐标系在viewport 中是否完全可见,同时也可以指定它在viewport 坐标系统中的位置 -->
<svg width="100" height="200" viewBox="0, 0, 100, 100" preserveAspectRatio="xMinYMin meet">
<circle cx="50" cy="50" r="49" stroke="black" stroke-width="1" fill="red" />
</svg>
<svg width="100" height="200" viewBox="0, 0, 100, 100" preserveAspectRatio="xMinYMin slice">
<circle cx="50" cy="50" r="49" stroke="black" stroke-width="1" fill="red" />
</svg>
</body>
</html>svg 组件
通过 defs 标签定义组件集合,其内部通过 g标签 或者 symbol标签 定义单个组件
use标签使用组件
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
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<style>svg { border: 1px solid black; }</style>
</head>
<body>
<h1>SVG 画弧</h1>
<svg width="50" height="100">
<defs>
<g id="arrow">
<polyline points="30 20, 70 50, 30 80" fill="transparent" stroke="currentColor" stroke-width="3"></polyline>
</g>
<symbol id="cross" viewBox="0 0 100 100" preserveAspectRatio="xMaxYMax meet">
<line x1="50" y1="10" x2="50" y2="90" stroke-width="8" stroke="currentColor"></line>
<line x1="10" y1="50" x2="90" y2="50" stroke-width="8" stroke="currentColor"></line>
</symbol>
</defs>
<use href="#arrow"></use>
<use href="#cross"></use>
</svg>
</body>
</html>
path 标签
该标签在简单用法里面有所提及,但是还存在两类减少使用的指令
画弧(
A rx ry x-axis-rotation large-arc-flag sweep-flag x y
):画笔从当前的点绘制一段圆弧到点(x,y)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
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<style>svg { border: 1px solid black; }</style>
</head>
<body>
<h1>SVG 画弧</h1>
<!-- 命令语法: A rx ry x-axis-rotation large-arc-flag sweep-flag x y
- rx 和 ry: 分别为椭圆弧的 x 和 y 方向的半径。
- x-axis-rotation: 椭圆的 x 轴相对于当前坐标系 x 轴的旋转角度。
- large-arc-flag: 0 表示选择较短的弧线,1 表示选择较长的弧线。
- sweep-flag: 0 表示弧线从起点到终点逆时针绘制,1 表示弧线从起点到终点顺时针绘制。
- x 和 y: 弧线终点的绝对坐标(使用 a 命令时为相对坐标)。 -->
<svg width="200" height="200" viewBox="0 0 500 500">
<path d="M 100 100 A 50 50 0 0 1 200 100"
style="fill:none;stroke:blue;stroke-width:2" />
</svg>
<svg width="200" height="200" viewBox="0 0 500 500">
<!-- 画一个完整的椭圆弧 -->
<path d="M 150 150 A 75 50 0 1 1 150 50"
style="fill:none;stroke:red;stroke-width:2" />
</svg>
<svg width="200" height="200" viewBox="0 0 500 500">
<!-- 画一个四分之一圆弧 -->
<path d="M 300 200 A 100 100 0 0 0 400 300"
style="fill:none;stroke:green;stroke-width:2" />
</svg>
<svg width="200" height="200" viewBox="0 0 500 500">
<!-- 画一个有旋转角度的弧 -->
<path d="M 150 150 A 75 50 45 1 1 250 250"
style="fill:none;stroke:blue;stroke-width:2" />
</svg>
</body>
</html>贝塞尔曲线(
C x1 y1, x2 y2, x y
): 画笔从当前的点绘制一段三次贝塞尔曲线到点(x,y)关于该点实际中尚未使用到,推荐看看张鑫旭大佬的这篇文章 深度掌握SVG路径path的贝塞尔曲线指令
注意事项
在JavaScript中创建SVG元素时,需要使用
document.createElementNS
方法而不是普通的document.createElement
方法,是因为SVG元素属于XML命名空间,而不是HTML命名空间。1
2const el = document.createElementNS("http://www.w3.org/2000/svg", name);
el.setAttribute("fill", "transparent");
应用
字体图标
通过 symbol 标签实现svg雪碧图,详见:vue3使用记录
symbol 标签和 g 标签,放在 defs 里都是在定义一个可复用的模块,那么两者之间有什么区别呢?在我的理解里,symbol 相对于g 标签最大的不同在于 symbol 可以给可复用代码块增加视图属性(width,height)和视口属性(viewBox)。方便在复用的时候直接调整到合适的尺寸(这也是为什么svg雪碧图和iconfont都是用 symbol 标签实现)。
svg动画
svg动画主要有 2 种实现方式,CSS 和 SMIL,详见:https://www.youbaobao.xyz/datav-docs/guide/libs/svgAnimation.html
实际工作中一般会通过 第三方库 来制作复杂动画,如 lottie-web
基于 CSS 实现
CSS 实现基于 CSS 动画三剑客(animation, transform, transition)的基础上,对一些属性进行控制
CSS可描述属性: 很多文章告诉我们 CSS 可以控制 SVG 去做动画,但是实际开发过程中我们会更想知道,到底哪些属性我们可以做css控制,这里给大家列出一些常用属性并且可以放心使用的属性
CSS 可控属性名 可实现场景 理论上所有的显示属性,都可以使用 CSS 控制包括:比如 stroke-width、color、fill 等等 SVG 的显示属性 大部分的显示样式动态变化 x 我们知道矩形有 x、y 属性,其含义是起始点,控制 x,我们可以动态控制矩形的X轴位移 y 控制 y,我们可以动态控制矩形的 Y 轴向位移 cx <circle cx="100" cy="100" r="50" fill="#fff" />
这是一个圆形,控制 cx 可以控制圆形(或者椭圆)的 X 轴位移cy 控制 cy 可以控制圆形(或者椭圆)的 Y 轴位移 r r 是圆的半径,控制 r 可以控制圆形的大小 rx rx 是椭圆的 X 轴方向半径,控制 rx 可以控制椭圆的大小 ry ry 是椭圆的 Y 轴方向半径,控制 ry 可以控制椭圆的大小 d path 标签的 d 属性,控制 d 的路径信息,可以控制图形的变幻(**d 属性在 safari 上是不支持 css 描述的。我们下文会详细的说明**) PS:如果各位看官们在日常开发中,不清楚该属性是否可以通过 css 去控制,这边给大家提供一个查询链接 不支持 CSS 控制的 SVG 相关属性 案例1:环形进度条
核心:stroke-dasharray
修改进度条起始位置: transform=”matrix(0,-1,1,0,0,440)”
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
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<style>
.circle {
animation: circle 5s linear infinite;
}
@keyframes circle {
from {
stroke-dasharray: 0 1068; /* 圆的周长 2 * 3.1415926 * 170 = 1068.141484 */
}
to {
stroke-dasharray: 1068 0;
}
}
.rect-process {
animation: rect-process 5s linear infinite;
}
@keyframes rect-process {
from {
stroke-dasharray: 0 800; /* 矩形的周长 4 * 200 = 800 */
}
to {
stroke-dasharray: 800 0;
}
}
</style>
</head>
<body>
<h1>CSS动画:环形进度条</h1>
<!-- 通过 matrix 将起始点移动到其他位置 -->
<svg width="440" height="440" viewbox="0 0 440 440">
<circle cx="220" cy="220" r="170" stroke-width="50" stroke="#D1D3D7" fill="none"></circle>
<circle
class="circle"
cx="220"
cy="220"
r="170"
stroke-width="50"
stroke="#00A5E0"
fill="none"
transform="matrix(0,-1,1,0,0,440)"
/>
</svg>
<svg width="200" height="200" viewBox="0 0 200 200">
<rect x="0" y="0" width="200" height="200" fill="none" stroke="#D1D3D7" stroke-width="50"></rect>
<rect x="0" y="0" width="200" height="200" fill="none" stroke="#00A5E0" stroke-width="50" class="rect-process" transform="matrix(0,1,-1,0,200,0)"></rect>
</svg>
</body>
</html>案例2:LOGO 描边
核心:stroke-dasharray、stroke-dashoffset
通过 getTotalLength API获取到路径长度
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
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<style>
.taobao-path {
fill: none;
stroke: #333;
stroke-width: 5;
animation: taobao 5s linear infinite forwards;
}
@keyframes taobao {
from {
stroke-dasharray: 5915.5;
stroke-dashoffset: 5915.5;
}
to {
stroke-dasharray: 5915.5;
stroke-dashoffset: 0;
}
}
</style>
</head>
<body>
<h1>CSS动画:LOGO描边</h1>
<svg viewBox="0 0 1024 1024" width="200" height="200">
<path class="taobao-path" d="M500.7104 1021.1328c-126.3616 0-244.992-45.056-334.0288-126.8992a434.0736 434.0736 0 0 1-101.8368-139.1872 409.728 409.728 0 0 1-37.4784-171.8528c0-70.7328 17.4848-138.6496 51.968-201.9072 31.36-57.4208 76.4672-109.056 130.8672-149.7856l0.0512-0.1792 6.9632-5.1712a194.56 194.56 0 0 0 24.5504-21.4528c10.752-12.3648 19.0976-24.448 24.1664-34.9952 2.6112-5.9904 4.5056-12.0576 5.632-18.048l0.1792-1.664c0.4864-13.568-0.1536-24.96-1.9712-34.816a171.4176 171.4176 0 0 0-7.936-23.4496 124.928 124.928 0 0 0-16.0512-23.3984c-4.096-4.864-16.4864-19.5584-13.7216-37.376 1.4592-9.3696 6.7328-17.3056 14.848-22.3232l3.328-2.048 3.7888-0.9216c1.8944-0.4608 5.1712-1.024 9.984-1.024 6.8096 0 14.6688 1.1008 23.296 3.3024l0.4096 0.1024c16.6912 4.5056 36.1984 13.0048 57.9328 25.2672l0.3328 0.2048c40.192 23.552 93.4912 53.12 149.4528 73.0368 14.6432 4.4288 31.0016 9.1904 48.6912 14.1824l2.6368 0.6144c2.176 0.512 4.2496 0.9984 6.144 1.3568 28.5696 5.5296 57.856 13.056 87.0912 22.3744a507.7248 507.7248 0 0 1 51.6352 15.5648c88.7808 32.256 164.0448 87.936 217.6 161.024 55.2192 75.3664 83.2512 163.9424 81.0752 256.2048a419.4304 419.4304 0 0 1-42.1888 173.824c-25.5232 52.7616-61.3376 100.096-106.496 140.6976-92.1088 82.8672-212.9408 128.5888-340.224 128.768h-0.6912z m-247.296-759.5264l-0.2048 0.6656-7.168 5.1712c-107.52 77.4656-169.1392 192.5632-169.1392 315.7504 0 213.6064 190.1056 387.4048 423.808 387.4048h0.6144c237.2096-0.2816 434.3552-177.024 439.4496-393.9584 3.84-163.4304-100.0192-307.7632-264.704-368.0256l-4.8896-1.7408-0.6656-0.256a459.1616 459.1616 0 0 0-42.88-12.6208l-1.6128-0.4608a724.7104 724.7104 0 0 0-82.3552-21.1712 210.944 210.944 0 0 1-7.9616-1.7664l-2.944-0.6656-1.2544-0.3328a2898.688 2898.688 0 0 1-50.688-14.7712l-1.1008-0.3584c-59.9808-21.2736-116.0448-52.352-158.208-77.056a326.4 326.4 0 0 0-15.2576-8.0896l0.0512 0.1024 0.4096 0.8192 0.3584 0.8448c4.8128 11.52 8.3712 22.144 10.9056 32.4352l0.3072 1.3312c2.6368 13.8496 3.584 29.2352 2.944 47.0272a79.6672 79.6672 0 0 1-0.5888 6.656l-0.2048 1.4848a135.0656 135.0656 0 0 1-9.472 30.7712l-0.3072 0.64c-7.168 15.104-18.048 31.0016-32.256 47.2832l-0.7168 0.768c-7.296 7.68-15.4368 15.104-24.2944 22.1184z" fill="#616161" p-id="12285"></path>
</svg>
<script>
const path = document.body.querySelector('.taobao-path')
const pathLen = path.getTotalLength()
console.log('pathLen', pathLen) // 5915.50146484375
</script>
</body>
</html>基于 SMIL 实现
不支持IE浏览器,虽然说早在 Chrome 45,chrome 就已经官宣要弃用 SMIL,但是到目前位置,各大浏览器厂商对它的支持度是这样的:https://caniuse.com/#search=SMIL
Chrom 宣布弃用 SMIL 是因为要支持 CSS Animation 与 Web Animation 的发展,所以我们可以理解为当前是在一个过渡状态,确实有一些暂时CSS 还没法支持或者支持度很差的动画效果,SMIL 可以轻松完成。但是基于 web 动画技术发展的大趋势,还是建议我们 SVG 动画实现方案的选择优先级是CSS 驱动 -> JS 驱动(我们可以采用一些框架,文末会给大家推荐一些好用的框架) -> SMIL 驱动
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<style>svg {border: 1px solid black;}</style>
</head>
<body>
<h2>set 标签:实现属性的延迟设置</h1>
<svg width="400" height="150">
<rect x="0" y="0" width="100" height="100" fill="red">
<set attributeName="x" attributeType="XML" to="10" begin="1s" />
<set attributeName="x" attributeType="XML" to="30" begin="2s" />
<set attributeName="x" attributeType="XML" to="60" begin="3s" />
<set attributeName="x" attributeType="XML" to="120" begin="4s" />
<set attributeName="x" attributeType="XML" to="240" begin="5s" />
</rect>
</svg>
<h2>animation 标签:相当于CSS的 animation</h1>
<svg width="400" height="200" viewBox="0 0 500 200">
<circle cx="0" cy="0" r="30" fill="blue" stroke="black" stroke-width="1">
<animate attributeName="cx" from="0" to="200" dur="5s" repeatCount="indefinite" />
<animate attributeName="cy" from="0" to="200" dur="5s" repeatCount="indefinite" />
</circle>
</svg>
<svg width="400" height="200">
<polygon points="30 30 70 30 90 70 10 70" fill="#fcc" stroke="black">
<!-- attributeType="XML" 对 XML 属性进行变换 -->
<animate attributeName="points" attributeType="XML" to="50 30 70 50 50 90 30 50" dur="5s" repeatCount="indefinite" />
</polygon>
</svg>
<h2>animationColor 标签:颜色切换,已废弃</h1>
<!-- https://developer.mozilla.org/en-US/docs/Web/SVG/Element/animate -->
<svg width="400" height="200" viewBox="0 0 400 200">
<rect x="0" y="0" width="100" height="50" fill="red">
<!-- fill="freeze": 动画执行后定在结束状态 fill="remove": 动画执行后回到初始状态(默认) -->
<animate attributeName="fill" from="red" to="blue" dur="5s" fill="freeze" repeatCount="1"></animate>
</rect>
</svg>
<h2>animateTransform 标签:相当于CSS的 animation</h1>
<svg width="400" height="200" viewBox="0 0 400 200">
<rect x="0" y="0" width="60" height="60" fill="red">
<animateTransform attributeName="transform" begin="0s" dur="3s" type="scale" from="1" to="2" fill="freeze" repeatCount="indefinite" />
</rect>
</svg>
<h2>animateMotion 标签:按 path 轨迹运动</h1>
<svg width="400" height="200" viewBox="0 0 400 200">
<rect x="0" y="0" width="10" height="10" fill="red">
<animateMotion
path="M 10 10 L 110 10 L 110 110 L 10 110 Z"
dur="5s"
rotate="auto"
fill="freeze"
repeatCount="indefinite"
/>
</rect>
<path id="motion-path" d="M 10 10 L 110 10 L 110 110 L 10 110 Z" fill="none" stroke="green" />
</svg>
<h2>SMIL支持混合动画</h1>
<p>该案例不是特别好理解,了解即可,主要为了说明可以在svg标签内定义多个动画效果</p>
<svg viewBox="0 0 400 200" width="400" height="200">
<rect x="0" y="0" rx="0" ry="0" width="10" height="10" fill="red">
<animateMotion
id="forward-rect"
path="M 10 10 L 110 10 L 110 110 L 10 110"
dur="2s"
rotate="0"
fill="freeze"
begin="0; backward-rect.end + 0.5s"
/>
<animateMotion
id="backward-rect"
path="M 10 110 L 110 110 L 110 10 L 10 10"
dur="2s"
rotate="0"
fill="freeze"
begin="forward-rect.end + 0.5s"
/>
<animate
id="red-to-blue"
attributeType="XML"
attributeName="fill"
begin="0; blue-to-red.end + 1s"
from="red"
to="blue"
dur="2s"
fill="freeze"
/>
<animate
id="blue-to-red"
attributeType="XML"
attributeName="fill"
begin="red-to-blue.end + 1s"
from="blue"
to="red"
dur="2s"
fill="freeze"
/>
</rect>
<path d="M 10 10 L 110 10 L 110 110 L 10 110" fill="none" stroke-width="1" stroke="blue"/>
</svg>
<h2>SMIL支持点击触动画</h1>
<svg viewBox="0 0 400 200" width="400" height="200">
<g id="rect1">
<rect x="0" y="0" rx="0" ry="0" width="100" height="100" fill="red">
<animate
attributeType="XML"
attributeName="fill"
from="red"
to="green"
begin="rect1.click"
dur="2s"
fill="freeze"
/>
</rect>
</g>
<animateTransform
attributeType="XML"
attributeName="transform"
type="translate"
from="0, 0"
to="50, 50"
begin="rect1.click"
dur="2s"
fill="freeze"
/>
<rect x="0" y="100" width="100" height="100" fill="blue">
<animate
attributeType="XML"
attributeName="fill"
from="blue"
to="green"
begin="rect1.click"
dur="2s"
fill="freeze"
/>
</rect>
</svg>
</body>
</html>
loading
1 | <svg width="200" height="200" viewBox="0 0 50 50"> |
flyBox
1 |
|
实用工具
termtosvg 将终端会话录制为 SVG 动画
svgo SVG压缩
参考文章
都2022年了,一个还不知道Lottie动画的前端已经OUT啦!
本文链接: http://www.ionluo.cn/blog/posts/76a1b131.html
版权声明: 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!