- // 第一行:包含了 XML 声明,XML 声明其实和 HTML 文档的 DTD 声明是类似的。
- // SVG 的文档声明方式(划重点:一般如果 SVG 运用在 HTML 里,我们可以不写这样的文档声明,但如果是单独的 SVG 文件,那就需要写了,否则浏览器可能会不认识)
- // standalone 属性是在表明该 xml 声明是否是独立的,如果不是即 standalone="no",那后面会引入外部的 dtd ,如第二行第三行所示。 version 属性用于指明 SVG 文档遵循规范的版本。 它只允许在根元素<svg> 上使用。 它纯粹是一个说明,对渲染或处理没有任何影响。虽然它接受任何数字,但是只有1.0 和 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">
-
- // xmlns 属性是 SVG 的 XML 声明空间
- // width & height 属性,可以理解成画布的大小
- // viewBox 属性的四个参数,前两个表示截图起点,后面两个表示截图终点,均是以左上角定点为原点
- <svg width="300"
- height="300"
- viewBox="0, 0, 100, 200"
- xmlns="http://www.w3.org/2000/svg"
- version="1.1">
- <circle cx="100" cy="50" r="49" stroke="black"
- stroke-width="2" fill="red" />
- </svg>
- <path d="
- M73.8616812,68.8664775
- L74.5015359,74.5939423
- L68.1746283,71.7969507
- C66.2299599,72.4159872 64.1377269,72.7711218 61.9444643,72.7711218
- C51.9719158,72.7711218 43.8883163,65.7823167 43.8883163,57.1611168
- C43.8883163,48.5399169 51.9719158,41.5511118 61.9444643,41.5511118
- C71.9164005,41.5511118 80,48.5399169 80,57.1611168
- C80,61.8286883 77.6181486,66.006419 73.8616812,68.8664775"
- id="Fill-1"
- fill="#FFFFFF">
- </path>
| 指令 | 参数 | 含义 |
|---|---|---|
| M | x,y | 将画笔移动到点(x,y) |
| L | x,y | 画笔从当前的点绘制线段到点(x,y) |
| H | x | 画笔从当前的点绘制水平线段到点(x,y0),y0 表示绘制前画笔所在 y 轴坐标,也就是 y 轴不变 |
| V | y | 画笔从当前的点绘制竖直线段到点(x0,y),x0 表示绘制前画笔所在 x 轴坐标,也就是 x 轴不变 |
| A | rx ry x-axis-rotation large-arc-flag sweep-flag x y | 画笔从当前的点绘制一段圆弧到点(x,y) |
| C | x1 y1, x2 y2, x y | 画笔从当前的点绘制一段三次贝塞尔曲线到点(x,y) |
| S | x2 y2, x y | 特殊版本的三次贝塞尔曲线(省略第一个控制点) |
| Q | x1 y1, x y | 绘制二次贝塞尔曲线到点(x,y) |
| T | x y | 特殊版本的二次贝塞尔曲线(省略控制点) |
| Z | 无 | 绘制闭合图形,如果 d 属性不指定Z命令,则绘制线段,而不是封闭图形 |
每个指令都有对应的小写指令。例如M 10,10 有对应的 m 10,10。大写代表绝对位置,所谓绝对位置即对 SVG 画布左上角原点的绝对。小写代表相对位置,所谓相对位置是以当前画笔所在位置进行定位。
| 图形 | 标签 | 模板 | 含义 |
|---|---|---|---|
| 矩形 | < rect > | <rect x="60" y="10" rx="10" ry="10" width="30" height="30" /> | x:起点横坐标,y:起点纵坐标,rx:倒角x轴方向半径,ry:倒角x轴方向半径,width:宽度,height:高度 |
| 圆形 | < circle > | <circle cx="100" cy="100" r="50" fill="#fff" /> | cx:圆心横坐标,cy:圆心纵坐标,r:半径 |
| 椭圆 | < ellipse > | <ellipse cx="75" cy="75" rx="20" ry="5" /> | cx:椭圆心横坐标,cy:椭圆心纵坐标,rx:椭圆x轴方向半径,ry:椭圆y轴方向半径 |
| 直线 | < line > | <line x1="10" x2="50" y1="110" y2="150" /> | x1,y1:起点,x2,y2:终点 |
| 折线 | < polyline > | <polyline points="60 110, 65 120, 70 115, 75 130, 80 125, 85 140, 90 135, 95 150, 100 145" /> | 每两个点以空格配对为一个坐标点,逗号隔开形成坐标集合。连成折线。 |
| 多边形 | < polygon > | <polygon points="50 160, 55 180, 70 180, 60 190, 65 205, 50 195, 35 205, 40 190, 30 180, 45 180" /> | 类似折线,不同的是,最后一个点会自动闭合第一个点,形成闭环。 |
symbol的作用就是相当于是一个元件,放在我们的工具箱里,放一份就可以无限引用。当它在工具箱里时,我们是看不到它的(页面不会渲染它),只有我们使用了<use>标签对其进行实例引用时,我们才可以在页面上看到它
- <svg class="svg-sprite">[工具箱]
- <symbol id="icon-wave_add" viewBox="0 0 76 76">
- <path d="M38 0a4 4 0 014 4v30h30a4 4 0 110 8H41.999L42 72a4 4 0 11-8 0l-.001-30H4a4 4 0 110-8h30V4a4 4 0 014-4z" fill="currentColor" fill-rule="evenodd" opacity="none"></path>
- </symbol>
- <symbol id="icon-time" viewBox="0 0 10 10">
- <path d="M5 0a5 5 0 110 10A5 5 0 015 0zm0 1.5a.5.5 0 00-.5.5v3.02l.008.088a.5.5 0 00.238.343L7.02 6.794l.082.039a.5.5 0 00.603-.215l.039-.082a.5.5 0 00-.216-.603L5.5 4.735V2l-.008-.09A.5.5 0 005 1.5z" fill="rgba(153,153,153,1)" fill-rule="evenodd" class=" "></path>
- </symbol>
- <symbol id="icon-wave_delete" viewBox="0 0 40 40">
- <g fill="none" fill-rule="evenodd">
- <circle fill="#000" opacity="0.2" cx="20" cy="20" r="20"></circle>
- <path stroke="#FFF" stroke-width="4" stroke-linecap="round" d="M13 13l14 14M27 13L13 27"></path>
- </g>
- </symbol>
- </svg>
-
-
- <use xlink:href="#icon-time"></use>
SVG 中有很多属性我们是可以用 CSS 去描述的。在基于 CSS 动画三剑客(animation, transform, transition)的基础上。我们对一些属性进行控制,就达到我们想要的动画效果。
- // 行内的 transform 属性,他的执行基点是在我们 svg 元素的左上角也就是 svg 的坐标原点
- <rect transform="rotate(45deg) ..." ... />
-
- // CSS 的 transform 原点则在元素本身的中心点
- rect {
- transform: rotate(45deg)
- }
也可以利用 SMIL 对 SVG 做动画处理
- <svg width="100%" height="100%" viewBox="0 0 100% 100%">
- {[1, 2, 3, 4, 5].map((it, index) => (
- <line
- key={index}
- stroke="#000"
- strokeWidth="2"
- x1={15 + index * 5}
- y1="8"
- x2={15 + index * 5}
- y2="22"
- >
- <animate
- attributeName="y1"
- values="8; 15; 8"
- dur="1s"
- begin={`${(5 % (index + 1)) * 0.2}s`}
- repeatCount="indefinite"
- />
- <animate
- attributeName="y2"
- values="22; 15; 22"
- dur="1s"
- begin={`${(5 % (index + 1)) * 0.2}s`}
- repeatCount="indefinite"
- />
- </line>
- ))}
- </svg>
通过对<path/> d 属性的控制,我们可以实现很多动画效果
- path {
- transition: ease all 0.3s; // 就像对dom一样的对待svg
-
- &.play { // 这里是播放状态下的<path />路径
- d: path("M 12,26 18.5,22 18.5,14 12,10 z M 18.5,22 25,18 25,18 18.5,14 z");
- }
-
- &.pause { // 这里是播放状态下的<path />路径
- d: path(
- "M 12,26 16.33,26 16.33,10 12,10 z M 20.66,26 25,26 25,10 20.66,10 z"
- );
- }
- }
- const pathMap = {
- from: "M 12,26 16.33,26 16.33,10 12,10 z M 20.66,26 25,26 25,10 20.66,10 z",
- to: "M 12,26 18.5,22 18.5,14 12,10 z M 18.5,22 25,18 25,18 18.5,14 z"
- };
-
- <svg class="icon" viewBox="0 0 120 120" width="400" height="400">
- <path
- d="M 12,26 16.33,26 16.33,10 12,10 z M 20.66,26 25,26 25,10 20.66,10 z"
- fill="#000000"
- >
- <animate
- attributeName="d"
- from={play ? pathMap.from : pathMap.to}
- to={play ? pathMap.to : pathMap.from}
- dur="0.3s"
- begin="indefinite" // 这里设置开始时间为无限以达到不自动播放的效果
- fill="freeze"
- />
- </path>
- </svg>
描边动画的核心点就在于 SVG 的两个显示属性分别是 stroke-dasharray、stroke-dashoffset
| 属性 | 值 | 描述 | 支持范围 |
|---|---|---|---|
| stroke-dasharray | 1 3 4 4 | 它的值是一个序列,可以传入多个值,分别指定描边短线的长度和描边线间距,多个值依次循环,如果传入3个值,类似于 stroke-dasharray: 1,2,3。则会自动复制一份再生效 | <circle>,<ellipse>,<line>,<mesh>,<path>,<polygon>,<polyline>,<rect>,<altGlyph>,<altGlyphDef>,<glyph>,<glyphRef>,<textPath>,<text>,<tref>,<tspan> |
| stroke-dashoffset | 10 | 描边线段的起始位置距离图形绘制起点的偏移量。正负值可以决定顺时针还是逆时针走向 | 跟stroke-dasharray一致 |
在实际开发中,我们会遇到一些比较复杂的图形需要做描边,这个时候我们没办法去得到它的周长是多少,这时候分两种场景处理。一种是在CSS里我们可以将stroke-dasharray的第二个值设置成一个非常大的数字,然后再去调整第一个值比如:
- path {
- stroke-dasharray: 0(调整到合适的值) 99999999999999
- }
如果在js里我们需要动态去获取周长的话,SVG提供了原生的api可以去获取path的周长。
- const inPath = document.getElementById("inner-path");
- console.log(inPath.getTotalLength());
- // 我们将整个飞机图形元件用g标签包起
- <g transform="translate(-100, -35) scale(0.1)">
- <path
- d="M164.485419 578.709313L196.274694 794.731891c0.722484 5.53904 8.188147 7.224835 11.078081 2.408278l75.860772-121.377234 740.063969-363.409219L164.485419 578.709313z"
- fill="#F68206"
- ></path>
- <path
- d="M2.167451 440.233302l159.668861 132.214487 857.828787-260.334901zM289.475071 679.375353l191.217309 153.407337 542.344309-518.743179z"
- fill="#FF9900"
- ></path>
- <path
- d="M204.222013 800.030103l125.23048-80.677328-48.888053-39.014111-76.342427 118.4873"
- fill="#D3650B"
- ></path>
-
- // 然后在这里,我们利用animateMotion,去做这个轨迹运动
- <animateMotion
- path="M 0 450 Q 150 50 250 50 Q 350 0 400 50 Q 500 50 450 200 C 300 350 250 200 500 50 C 600 50 750 200 650 250 A 50 50 0 1 1 800 50 "
- begin="0s"
- rotate="auto"
- dur="20s"
- repeatCount="indefinite"
- />
- </g>
GSAP、Snap.svg、SVG.js、Velocity.js、anime.js、D3