前言
在日常开发过程中,我们或多或少都接触过SVG
,有可能是用它来画一些简单的图形,有可能是使用它来构建工程的字体文件库,甚至是用它来绘制一些复杂的可视化模块。本文会详细介绍SVG
的基本图形以及常见的动画形式,帮助你了解入门SVG
。
基本图形
下面会介绍SVG
预设的数种形状以及对应的属性介绍,在实际开发或者设计过程中,绘画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
:矩形边框的不透明度
圆形
circle
标签属性解释如下:cx
、cy
:圆心坐标r
:圆的半径
椭圆
ellipse
标签属性解释如下:cx
:圆心的x
坐标cy
:圆心的y
坐标rx
:水平半径ry
:竖直半径
线条
line
标签属性解释如下:x1
:线段的起点x
坐标x2
:线段的终点x
坐标y1
:线段的起点y
坐标y2
:线段的终点y
坐标
多边形
polygon
标签属性解释如下:points
:定义多边形的N
个(x
,y
)坐标,不少于三个点
折线
polyline
标签属性解释如下:points
:定义曲线的N
个(x
,y
)坐标
PS:你看这个曲线图,像不像你最近的股票基金路径
path
标签中用属性d
来描述路径,花样非常多,我们一个一个来看。M
moveto
,看意思是移动到某个点,想象你手里拿着一根笔,M命令就是让你的笔尖移动到某个点,准备开始绘画。语法为M(x,y)
,其中大写M
表示绝对定位,小写m
表示相对定位,下面的其他语法也一样。Z
closepath
,闭合你前面所画的路径。L
lineto
,意思为画一条线,语法为L(x,y)
。
H
horizontal lineto
,水平地画一条线。语法为H(y)
V
vertical lineto
,竖直地画一条线。语法为V(x)
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命令,它的两个控制点会被视为同一个点。
Q
二次贝塞尔曲线,只需要一个控制点。语法为
Q x1 y1, x y
T
与
S
类似,通过前一个控制点,推断出新的控制点。语法为T x y
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
编辑器来辅助绘画图形。SVG
动画
在上面我们了解了各种SVG
的图形及语法,SVG
除了可以画出各种各样的图形之外,它的动画效果也是别树一帜的。我们下面来了解一下SVG
常见的动画形式以及应用。描边动画 下面先介绍两个属性,
stroke-dasharray
和stroke-dashoffset
。我们实现的描边动画是围绕这两个属性展开的。stroke-dasharray
用于创造虚线,语法为
stroke-dasharray:n1 [n2 [n3]]
。什么意思呢?先来看下面几个例子【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
表示虚线的偏移量,值为正数时表示向反方向偏移,值为负数表示向正方向偏移。
由图可以看出,我们把路径分成了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图形如下,同样也可以做一个描边动画。
在开始之前,先介绍
path
元素中的一个方法——getTotalLength()
,该方法返回路径总长度(以用户单位为单位)的计算值。我们可以利用它计算任意path
元素的总长度。有了上述的知识之后,我们大概做了一个上面的动画,这里是实现的代码
.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;
}
}
路径跟随动画
路径跟随动画是另一种
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)
加上角度偏移后,可以感觉得到路径的贴合度更高,视觉效果更好。