前言
最近在实现VUI库的基础组件progress,其中涉及到圆形进度相关的绘制,参考了element ui框架的实现发现是通过svg来实现的。
实际上自身之前对于svg仅仅是知道的水平,本文旨在提交自身对于svg路径path以及动画相关内容的了解。
本文内容主要分为三块:
- viewBox属性
- svg路径path相关知识学习
- stroke-dasharray和stroke-dashoffset属性学习
viewBox属性
首先来学习viewBox属性的,该属性是用于呈现内容区域的,常用于svg等标签上。
viewBox属性存在4个值:
viewBox=“x y width height”
其中x/y表示起始点坐标,width/height表示呈现区域的宽高。
viewBox属性存在的特点:
- 当svg的父标签的width或height小于viewBox的设置,svg会等比调整到svg父标签width/height中最小值
- 当svg的父标签的width或height大于viewBox的设置,svg会等比调整到svg父标签width/height中最大值
即总是以适合的比例来呈现内容
实例1:
父标签width设置为50px,viewBox的width/height统一设置成100
实例2:
父标签width设置成300px,viewBox的width/height统一设置成100
实例3:
viewBox属性前2个属性值即起始坐标设置的呈现效果
从上面可以看出,当设置x/y值是,实际上svg中的内容在x轴方向相对右侧向左移动了10px,y轴方向上相对底部向上移动了10px。
实际上x/y也可以设置负值,负值在x轴方向会相对左侧向右移动一定像素,y轴的则如正值的相反。
一般场景,viewBox中的x/y都是设置为0
<path>标签
svg中
该标签提供一些绘制命令,使用d属性来存储路径命令数据,命令主要如下:
-
M/m:移动命令,表示绘制轮廓的起始位置
-
L/l:绘制直线到指定坐标
-
V/v:垂直方向绘制直线到指定坐标
-
H/h:水平方向绘制直线到指定坐标
-
A/a:绘制弧线
A/a: rx ry xr laf sf x y
- rx、ry:表示圆弧的x和y方向的半径
- xr:圆弧横轴相对坐标系横轴的偏移角度
- laf:取大角度弧还是小角度弧,1表示大角度弧,0表示小角度弧
- sf:顺时针还是逆时针,1表示顺,0表示逆
- x、y:弧的终点坐标
需要注意的是:
大写命令的坐标表示绝对坐标,小写命令的坐标表示相对坐标
实例1:
通过path + circle来查看绘制起点的位置问题
path绘制一个圆,circle是用于标识指定坐标。
从上面可以看出,在svg viewBox为100即父容器恰好可以呈现的情况下,(50,50)实际上对应path绘制圆的顶点,那这里是不是可以认为:
path中M移动位置即依据绝对坐标来移动,是依据最最接近坐标原点的顶点为基础来进行的,即上图中圆黑点的位置
现在想要是圆点位置在svg的中心即黑点位置,要如何做呢?
<path
d="M 50 50 m 0 -47 a 47 47 0 1 1 0 94 a 47 47 0 1 1 0 -94"
stroke="#ebeef5"
stroke-width="2.0"
fill="none">
</path>
需要解释下d中内容:
M 50 50 m 0 -47 a 47 47 0 1 1 0 94 a 47 47 0 1 1 0 -94
-
M 50 50:表示已绝对坐标移动到(50,50)
-
m 0 -47:表示已相对坐标延y轴方向上向上移动47px,即顶点位置在(50, 3)处
-
a 47 47 0 1 1 0 94:
以相对坐标绘制圆弧,这里47表示半径,为什么是47呢?
实际上这里半径是(viewbox的width/2 - 边框的strokeWidth)得到的,即实际上这里也可以是 50 - 2即48
以当前点位置为起始点即(50,3),终点的绝对坐标是(50,94),即从(50, 3)以半径47逆时针画弧到(50,94)
这里实际上需要专注第3个参数,该参数表示圆弧横轴相对坐标系横轴的偏移角度,需要注意的是:
实际上当x轴半径和y轴半径相同,即形成圆时,该参数不论设置为任何值都没有效果
实例2:
查看path中a中第三个参数的影响效果
从上面实例可知道:
path a的第三个参数需要在圆弧x轴和y轴半径不对时,才会有效果
storke-dasharray和stroke-dashoffset
首先先介绍stroke-dasharray属性,该属性是用于设置边框的实虚线的即哪里是实线,哪里是虚线。
实例1:
上面例子中设置了stroke-dasharray为1px,这里整个直线原始长度为10px(在上面中实际上被放大了10倍,即100px)。从上图中可知实线区域是5个即5px,虚线也是5个即每1px有一个实线区域。
- 当设置stroke-dasharray为2px时,实线区域数目为3
- 当设置stroke-dasharray为3px时,实线区域数目为2
- 当设置stroke-dasharray为4px时,实线区域数目为2
- 当设置stroke-dasharray为5px时,实线区域数目为1
- 当设置stroke-dasharray为6px时,实线区域数目为1
上面说的实线区域数目都是指viewBox即可视区域展示出来的实线数目,由上面的逻辑可知实线数目在可视区域是可被计算出来的:
实线数目计算值 = 直线原始长度 / (2 * stroke-dasharray定义的虚线宽度)
实线数目 = Number.isInteger(实线数据计算值) ? 实线数目计算值 : parseInt(实线数据计算值 + 1, 10);
实际上:
stroke-dasharray是设置虚线宽度,当虚线宽度被增加,在一定可视范围内,就会呈现出虚线宽度增加,实线宽度也会增加的效果,即拉伸的效果
线条动画正是基于拉伸效果产生的
紧接着介绍stroke-dashoffset属性,线条动画另一个关键属性,该属性是设置dasharray数组呈现时的偏移量。
stroke-dashoffset设置的效果是自身循环的,即不需要担心stroke-dashoffset设置超过总长度的问题。
实例2:
上面是stroke-dashoffset设置为0时的显示情况,当stroke-dashoffset设置1px时,具体呈现如下:
从上图中可知,当stroke-dashoffset设置为1px时,效果上向左移动了1px。
stroke-dashoffset支持正负数,效果上正数表示向左移动或逆时针移动,负数表示向右移动或顺时针移动
动画实例:进度条
使用stroke-dasharray + stroke-dashoffset实现一个小动画:直线进度条。
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
@keyframes progress-animate {
form {
stroke-dashoffset: 200px;
}
to {
stroke-dashoffset: 0px;
}
}
.progress {
stroke-dasharray: 200px;
stroke-dashoffset: 200px;
animation: progress-animate 3s infinite;
}
</style>
</head>
<body>
<div style="width: 100px;height: 100px;">
<svg>
<path
d="M 10 10 L 210 10"
stroke="#ddd"
stroke-width="8"
stroke-linecap="round">
</path>
<path
class="progress"
d="M 10 10 L 210 10"
stroke="rgb(64, 158, 255)"
stroke-width="8"
stroke-linecap="round">
</path>
</svg>
</div>
</body>
</html>
实现效果:
实际上上面简单动画核心原理在于:
设置stroke-dasharray >= 直线长度
设置stroke-dashoffset = 直线长度 * 进度百分比