本文主要介绍基于 NutUI Vue3 的
circleProgress
组件的设计与实现原理,是一个圆环形的进度条组件,用来展示操作的当前进度,支持修改进度以及渐变色。环形进度条是很常用的一个组件,特别是在管理后台数据统计的页面上或是一些需要用户等待的任务。
效果如下图
首先我们来理一下我们的需求,我们需要一个圆形的可以改变进度,有动画的,可以支持渐变色的进度条。
目前是有三种常见的实现方案:
CSS 实现
SVG 实现
Canvas 实现
而 SVG 又分为两种,一种是直接用 circle
实现,另外一种是用 path
来画出来。
然后我们来看下一些国内出名的组件库实现方式:
Antd Design | Tdesign | varlet | Element UI | vant | |
---|---|---|---|---|---|
SVG | Circle | Circle | Circle | Path | Path |
Antd Design和TDesign,varlet 采用svg的Circle实现,但是小编个人觉得Antd Design的渐变色的开始和结束是有问题的,TDesign和varlet暂时不支持环形进度条线性渐变
Element UI和Vant 采用svg的Path实现,Vant是支持线性渐变的,而且不存在渐变色的开始和结束不对应问题,Element暂时不支持线性渐变
目前主流组件库的环形进度条基本上都是用svg绘出来的,因为实现思路简单,使用 SVG 画两个圆 一个圆作为底色,另一个圆作为进度展示,使用时候的问题也很少。NutUI也选择使用SVG来实现进度条。
下边详细介绍一下两种实现方式
首先来说下 SVG 的 circle
(Antd,Tdesign的实现方式),下边也会介绍到Antd的进度条线性渐变问题。
首先第一步 我们先来画个最简单的圆
<svg height="100" width="100" x-mlns="http://www.w3.org/200/svg">
<circle r="40" cx="50" cy="50" stroke="'red'" stroke-width="10" fill="none" />
</svg>
上边的属性我就不多介绍了 不了解的同学其实也可以读出来大概意思,r 是半径,cx,cy 为圆点位置,以及颜色和弧度的宽度。
这里可能有的同学要问了 这不是一个 circle
就可以实现嘛,可不要忘记了我们需要的效果,
那我们再来画一个不是100%进度的圆,如何画呢
这里就用到了 stroke-dasharray
属性,可以将图形的描边进行点状化,这里需要理解的是,点状化的点,其大小是可以设置的,并不真的就是那么一个·,可以变长或者变短。
所以如果 circle
的点的长度正好等于 circle
边长,那么点看上去就是 circle
的边。
我们计算下圆环的周长就可以了,参数也就是弧长,极大值,极大值就是周长,弧长就是进度值。
大家应该也发现问题了,这样的话在不是100%进度的时候,部分弧度是没有颜色的,所以我们还需要一个底色。也就是另外一个circle
。
<svg>
<circle
r="40"
cx="50"
cy="50"
stroke="#d9d9d9"
stroke-width="10"
fill="none"
/>
/>
<circle
r="40"
cx="50"
cy="50"
stroke="red"
stroke-width="10"
stroke-dasharray="200,251"
fill="none"
stroke-linecap="round"
/>
</svg>
因为这里我们需要它从左边的中间位置开始,所以还需要加上旋转。
至于接下来就很简单了 让它动起来,那么如何动起来呢 动态改变 stroke-dasharray
的值就可以了,下方介绍path
的时候会讲到如何改变。来讲一下遇到的小坑 ,就是我们在做渐变色的时候,会发现我们的渐变色并不是从我们的进度开始地方开始渐变的。
这里我们也可以看下Antd的环形进度条渐变,我用一个红到黑来给大家看一下。(Tdesign这里小编在在线编辑器里尝试了一下,线性渐变没有生效。)
这里小编个人觉得Antd的这个渐变也是不对的(仅代表个人想法),当然大家有别的想法也可以提出来一起探讨。
其实是因为线性渐变是从左往右的,并且上边为圆环加了旋转的原因,解决方法大家可以自行搜索引擎搜索一下,这里不多做阐述。
下边主要介绍一下用 path
(Vant,Element实现方法)实现吧 可以简单并完美的解决上边的渐变色对应不上(Element暂时不支持线性渐变)的问题。
和 circle
实现思路是一样的,画两个圆,一个用来表示底色,一个用来表示弧度,主要是我们如何来画一个圆呢
viewBox="0,0,100,100"
,其实它是将我们的整个画布的宽和高分为100份,其中SVG元素是在这个分割以后的画布上摆放展示。我们不需要再关注 SVG 的宽高,它现在已经实现了自适应,会自动根据外层父元素的宽高进行
适配,我们最外层给用户一个 Props 来设置环形进度条的大小。
<div :style="{ height: radius * 2 + 'px', width: radius * 2 + 'px' }">
<svg viewBox="0 0 100 100"></svg>
</div>
path
来画圆,那我们当然得熟悉一下的d
属性来,它可以画出各种各样的线来。d
属性用来定义路径数据,我们首先来了解下我们需要用到的参数: M = moveto(M X,Y) :将画笔移动到指定的坐标位置
A = elliptical Arc<