共计 7459 个字符,预计需要花费 19 分钟才能阅读完成。
前言
在日常开发过程中,咱们或多或少都接触过 SVG
,有可能是用它来画一些简略的图形,有可能是应用它来构建工程的字体文件库,甚至是用它来绘制一些简单的可视化模块。本文会具体介绍SVG
的根本图形以及常见的动画模式,帮忙你理解入门SVG
。
根本图形
上面会介绍 SVG
预设的数种形态以及对应的属性介绍,在理论开发或者设计过程中,绘画 SVG
图标大多数时候都是会应用一些工具的。
矩形
<svg width="200px" height="200px" version="1.1" xmlns="http://www.w3.org/2000/svg">
<rect width="100" height="100" x="20" y="20" rx="30" ry="30" style="fill:rgb(255,255,0);stroke-width:4;
stroke:rgb(0,0,0);fill-opacity: .6;stroke-opacity: .5;" />
</svg>
在 svg
标签中,width
示意整个宽度,height
示意高度,version
示意版本,xmlns
示意命名空间,前面两个属性是绝对固定的货色,稍作了解即可。
rect
中的标签属性与 style
属性解释如下:
width
:宽度height
:高度x
:程度方向上的偏移量y
:竖直方向上的偏移量rx
、ry
:定义圆角成果-
style
:款式(这些款式同样实用于上面的图形,所以上面的图形只会介绍属性,不会反复介绍款式)fill
:rgb 色彩,示意矩形的填充色彩fill-opacity
:填充的不透明度stroke
:rgb 色彩,示意矩形的边框色彩stroke-width
:矩形边框的大小stroke-opacity
:矩形边框的不透明度
圆形
<svg width="200px" height="200px" version="1.1" xmlns="http://www.w3.org/2000/svg">
<circle cx="100" cy="50" r="40" style="stroke:black; stroke-width:2; fill:blue" />
</svg>
circle
标签属性解释如下:
cx
、cy
:圆心坐标r
:圆的半径
椭圆
<svg width="200px" height="200px" version="1.1" xmlns="http://www.w3.org/2000/svg">
<ellipse cx="100" cy="50" rx="40" ry="50" style="fill:purple" />
</svg>
ellipse
标签属性解释如下:
cx
:圆心的x
坐标cy
:圆心的y
坐标rx
:程度半径ry
:竖直半径
线条
<svg width="200px" height="200px" version="1.1" xmlns="http://www.w3.org/2000/svg">
<line x1="10" y1="10" x2="100" y2="100" style="stroke:red;stroke-width:2" />
</svg>
line
标签属性解释如下:
x1
:线段的终点x
坐标x2
:线段的起点x
坐标y1
:线段的终点y
坐标y2
:线段的起点y
坐标
多边形
<svg width="200px" height="200px" version="1.1" xmlns="http://www.w3.org/2000/svg">
<polygon points="10,10 10,200 100,200 100,10" style="fill:red;
stroke:#000000;stroke-width:1" />
</svg>
polygon
标签属性解释如下:
points
:定义多边形的N
个(x
,y
)坐标,不少于三个点
折线
<svg width="200px" height="200px" version="1.1" xmlns="http://www.w3.org/2000/svg">
<polyline points="10,10 20,20 30,15 40,40 50,30 60,60" style="fill:transparent;stroke:green;stroke-width:2" />
</svg>
polyline
标签属性解释如下:
points
:定义曲线的N
个(x
,y
)坐标
PS: 你看这个曲线图,像不像你最近的股票基金
门路
path
标签中用属性 d
来形容门路,花色十分多,咱们一个一个来看。
M
moveto
,看意思是挪动到某个点,设想你手里拿着一根笔,M 命令就是让你的笔尖挪动到某个点,筹备开始绘画。语法为 M(x,y)
,其中大写M
示意相对定位,小写 m
示意绝对定位,上面的其余语法也一样。
Z
closepath
,闭合你后面所画的门路。
L
lineto
,意思为画一条线,语法为L(x,y)
。
<svg width="100%" height="100px" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="M10 10 L200 200 Z" style="stroke: red;stroke-width:2" />
</svg>
H
horizontal lineto
,程度地画一条线。语法为H(y)
<svg width="100%" height="100px" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="M10 10 H200 Z" style="stroke: red;stroke-width:2" />
</svg>
V
vertical lineto
,竖直地画一条线。语法为V(x)
<svg width="100%" height="100px" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="M10 10 V200 Z" style="stroke: red;stroke-width:2" />
</svg>
C
curveto
,三次贝塞尔曲线。语法为 C x1 y1, x2 y2, x y
。三次贝塞尔曲线应该有四个点来管制,C
中只形容了三个点,所以起始点是你用 M
形容的。举个例子:M10 10 C 100 100 150 100 200 10
,指的是起始点为 (10,10)
,第一个辅助点为(100,100)
,第二个辅助点为(150,100)
,终止点为(200,10)
管制的三次贝塞尔曲线。
S
smooth curveto
,用来形容对称的三次贝塞尔曲线。语法为S x2 y2, x y
。当 S 跟在 S 命令或者 C 命令之后时,它的第一个控制点(即 S 语法中省略掉的 x1 y1)会被假如为前一个控制点的对称点,如果后面没有跟 S 或者 C 命令,它的两个控制点会被视为同一个点。
<svg width="100%" height="100px" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="M10 10 S 100 100 200 10" style="stroke: red;stroke-width:1;fill:white" />
</svg>
<svg width="100%" height="200px" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="M100 100 C 150 150 200 150 250 100 S 300 20 400 100" style="stroke: red;stroke-width:1;fill:white" />
</svg>
Q
二次贝塞尔曲线,只须要一个控制点。语法为Q x1 y1, x y
<svg width="100%" height="200px" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0 Q100 100 200 0" style="stroke: red;stroke-width:1;fill:white" />
</svg>
T
与 S
相似,通过前一个控制点,推断出新的控制点。语法为T x y
<svg width="100%" height="200px" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="M10 80 Q 52.5 10, 95 80 T 180 80" stroke="red" fill="transparent" />
</svg>
A
elliptical Arc
,椭圆弧。语法为A rx ry x-axis-rotation large-arc-flag sweep-flag x y
rx
、ry
椭圆弧的两个半轴长度,如果相等就是圆弧x-axis-rotation
椭圆绝对于坐标系的旋转角度large-arc-flag
绘制优弧(1),劣弧(0)sweep-flag
顺时针绘制(1),逆时针绘制(0)x
y
圆弧起点坐标
<svg width="100%" height="100vh" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="M200 200 H300 V100 A 100 100 0 1 1 200 200" style="stroke: red;stroke-width:2;fill:white" />
</svg>
门路的绘制十分复杂。。倡议应用 SVG
编辑器来辅助绘画图形。
SVG
动画
在下面咱们理解了各种 SVG
的图形及语法,SVG
除了能够画出各种各样的图形之外,它的动画成果也是别树一帜的。咱们上面来理解一下 SVG
常见的动画模式以及利用。
描边动画
上面先介绍两个属性,stroke-dasharray
和stroke-dashoffset
。咱们实现的描边动画是围绕这两个属性开展的。
stroke-dasharray
用于发明虚线,语法为stroke-dasharray:n1 [n2 [n3]]
。什么意思呢?先来看上面几个例子
<svg width="100vw" height="100vh" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="M0 100 H400" class="line" style="stroke-dasharray: 10;"></path>
<path d="M0 200 H400" class="line" style="stroke-dasharray: 10 20;"></path>
<path d="M0 300 H400" class="line" style="stroke-dasharray: 10 20 30;"></path>
</svg>
配合上图,咱们能够总结一下 stroke-dasharray
的规定:
- 一个参数时形容实线虚线的长度相等,比方
stroke-dasharray: 10
就示意形容这条门路的时候,10 长度
的实线 ->10 长度
的虚线 ->10 长度
的实线···如此循环。 - 两个参数时形容实线长度是第一个参数,虚线长度是第二个参数,
stroke-dasharray: 10 20
示意10 长度
实线 ->20 长度
虚线 ->10 长度
实线···如此循环。 - 三个参数时形容实线虚线长度交替进行,
stroke-dasharray: 10 20 30
示意10 长度
实线 ->20 长度
虚线 ->30 长度
实线 ->10 长度
虚线···如此循环。
stroke-dashoffset
示意虚线的偏移量,值为负数时示意向反方向偏移,值为正数示意向正方向偏移。
<svg width="100vw" height="100vh" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="M0 100 H400" class="line" style="stroke-dasharray: 400;stroke-dashoffset:200"></path>
</svg>
由图能够看出,咱们把门路分成了 400 实线 400 虚线的展现模式,通过将虚线从反方向偏移 200,就能够把实线的 200 长度遮起来,就实现了图中只剩半截的门路。自此,咱们曾经能够利用这两个属性来做一个门路动画了,无非就是将这两个属性配合 CSS
的animation
动画而已。
先从简略的开始,比方像上面的直线从无到有动画:
.line {
stroke-width: 4px;
stroke: red;
stroke-dasharray: 400;
stroke-dashoffset: 400;
animation: move 2s;
}
@keyframes move {
to {stroke-dashoffset: 0;}
}
<svg width="100vw" height="100vh" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="M0 100 H400" class="line"></path>
</svg>
在有了上诉简略的从无到有动画之后,其实不难发现它不仅仅只能作用于一条简略的直线上,例如咱们当初有一个 SVG 图形如下,同样也能够做一个描边动画。
在开始之前,先介绍 path
元素中的一个办法——getTotalLength()
,该办法返回门路总长度 (以用户单位为单位) 的计算值。咱们能够利用它计算任意 path
元素的总长度。
有了上述的常识之后,咱们大略做了一个下面的动画,这里是实现的代码
<style>
.path {
stroke-width: 4px;
stroke: white;
stroke-dasharray: 5199.931640625;
stroke-dashoffset: 5199.931640625;
animation: move 2s linear;
fill: white;
animation-fill-mode: forwards;
}
@keyframes move {
to {
stroke-dashoffset: 0;
stroke: black;
transform: scale3d(1.1, 1.1, 1.1);
}
}
.scale {
stroke-dashoffset: 0;
stroke: black;
animation: scale-path .2s;
animation-fill-mode: forwards;
opacity: 0;
}
@keyframes scale-path {
0% {transform: scale3d(1, 1, 1);
fill: white;
opacity: 0;
}
50% {transform: scale3d(1.2, 1.2, 1);
}
100% {transform: scale3d(1, 1, 1);
opacity: 1;
fill: black;
}
}
</style>
<svg class="icon" viewBox="0 0 2000 2000" version="1.1" xmlns="http://www.w3.org/2000/svg"
width="200" height="200">
<path class="path" d="......"p-id="2506"></path>
</svg>
<script>
const path = document.querySelector('.path')
setTimeout(() => {$(path).addClass('scale')
}, 2000)
</script>
门路追随动画
门路追随动画是另一种 SVG
常见的动画模式,即让一个物体沿着 SVG
图形的门路挪动。开始之前先来理解一个 path
元素的办法:getPointAtLength(number):Point
,它的入参是一个数字,值为 0~getTotalLength()
,返回值为该长度对应下的点的坐标。理解了这个之后,咱们只有把门路长度分为N
份,能够取得 N
个点的坐标,让受控的元素的坐标逐渐的赋值为这 N
个点的坐标,就能够做到元素追随 SVG
门路挪动的成果,具体实现逻辑如下:
const STEP = 1000
let currentStep = 1
const path = document.querySelector('.path');
const length = path.getTotalLength();
const slider = document.querySelector('.slider')
setInterval(() => {const point = path.getPointAtLength(currentStep / STEP * length)
slider.style.top = point.y - 10 + 'px' //10 是滑块宽度的一半,为了更好的贴合门路
slider.style.left = point.x - 10 + 'px'
if (currentStep === STEP) {currentStep = 1} else {currentStep++}
}, 10)
在获取到具体坐标之后,咱们只须要用一个定时器把坐标一直赋值,就能够实现门路追随成果。然而在上述成果中,留神到滑块的角度是没有变动的,导致在圆弧局部视觉上并不能体现出追随的成果,也就是说咱们还需要求出滑块对应的旋转角度。
这里留神到咱们是把门路点分成了 N
个,因为点足够的多,坐标变动足够的快,所以咱们看起来是间断的,实际上挪动的时候还是离散的。计算机的抽样都是这样,只有抽样点足够的多,就会有限迫近平滑的曲线。
所以要求滑块在某一个点的角度,能够先求出滑块在此处的切线斜率。在这个情景下,假如以后点的坐标是 (x2,y2)
,上一个点的坐标是(x1,y1)
,那么k=(y2-y1)/(x2-x1)
这条线段的斜率,只有这条线段足够的短,就能够当作是滑块目前的斜率,求出斜率之后应用反正切三角函数 Math.atan
能够求出倾斜角,留神,这里得进去的角度单位是弧度,咱们还须要把它转成角度能力用在 rotate
中。
let prePos
setInterval(() => {
//......
if (prePos) {const k = (point.y - prePos.y) / (point.x - prePos.x)
const val = Math.atan(k) / Math.PI * 180
slider.style.transform = `rotate(${val}deg)`
}
prePos = point
//......
}, 10)
加上角度偏移后,能够感觉得到门路的贴合度更高,视觉效果更好。