本文主要介绍基于NutUIVue3的circleProgress组件的设计与实现原理。 是一个圆形进度条组件,用于显示当前操作的进度,支持改变进度和渐变颜色。 圆形进度条是一个非常常用的组件,尤其是在管理后台数据统计或者需要用户等待的任务的页面上。
达到疗效
效果如右图
实施思路
首先,我们来梳理一下我们的需求。 我们需要一个可以改变进度、有动画、可以支持渐变颜色的矩形进度条。
目前常见的实现有以下三种:
SVG分为两种,一种是直接使用圆来实现,另一种是使用路径来绘制。
那么我们来看看国外一些知名的组件库的实现方法:
AntdDesign和TDesign、varlet都是通过svg Circle实现的,个人认为AntdDesign的渐变颜色的开头和结尾有问题,TDesign和varlet暂时不支持圆形进度条的线性渐变
ElementUI和Vant使用svg Path来实现。 Vant支持线性渐变,不存在渐变颜色起点和终点不对应的问题。 Element暂时不支持线性渐变。
目前主流组件库的圆形进度条基本都是用svg绘制的。 由于实现思路简单,使用SVG绘制两个圆圈,一个圆圈作为背景色,另一个圆圈作为进度显示,所以在使用时存在很多问题。 很少。 NutUI还选择使用SVG来实现进度条。
下面详细介绍这两种实现方法。
循环实现
首先我们来说说SVG的圆(Antd的实现方法,Tdesign),下面也会介绍Antd的进度条的线性渐变问题。
第一步,画一个最简单的圆
上面的属性我就不多介绍了,虽然不了解的朋友也能看懂大概的意思,r是直径,cx,cy是点的位置,还有颜色和长度弧线。
这里有的朋友可能会问这个用圈子可以实现吗,别忘了我们需要的疗效,
那我们来画一个不是100%进度的圆,怎么画
这里使用了Stroke-DashArray属性,可以剥离图形的遮罩。 这里需要理解的是,切片点的大小是可以设置的,实际上并不是这样的。 它可以变长或变短。
因此,如果圆上一点的宽度恰好等于圆的周长,那么该点看起来就像圆的边缘。
我们可以估计环的边长,参数是弦长,最大值,最大值就是边长,弦长就是进度值。
您也应该注意到了这个问题。 这种情况下,当进度不是100%时,有些弧线会没有颜色,所以我们需要一个背景颜色。 那是另一个圈子。
/>
因为这里我们需要它从左边中间位置开始,所以我们还需要添加旋转。
至于连接起来,很简单,就是让它向上移动,那么如何向上移动呢? 动态改变strike-dasharray的值即可,如何改变在下面介绍路径时会提到。 说一下我们遇到的小坑,就是我们在做渐变色的时候,会发现我们的渐变色并不是从我们的进度开始的。
这里我们还可以看一下Antd的圆形进度条的渐变效果。 让我向您展示一款从红色到黑色的。 (Tdesign这里小编在在线编辑器中尝试了一下,线性渐变没有生效。)
这里小编个人认为Antd的渐进变化也是错误的(仅代表个人观点),其实大家也可以提出其他观点,一起解释。
虽然是因为线性渐变是从左到右,但是上面的环加上旋转的原因是大家可以在自己的搜索引擎上搜索一下解决方案,这里就不多讨论了。
路径实现
下面主要介绍path的实现(Vant、Element实现),可以简单完美的解决上述渐变颜色不匹配的问题(Element暂时不支持线性渐变)。
思路和圆一样,画两个圆,一个用来表示背景颜色,另一个用来表示弧度。 最主要的是我们如何画一个圆?
了解了viewBox属性,在SVG标签中添加这个属性,这个属性是用来设置画布的大小的,注意了,它是一个相对大小,会根据我们父元素的变化动态适应。 例如,我们将其属性设置为 viewBox="0,0,100,100",尽管它将我们整个画布的宽度和高度分为 100 部分,并且 SVG 元素显示在划分的画布上。
我们不需要再关注SVG的宽高了,它已经实现了自适应,会根据内部父元素的宽高手动调整
适配,我们最内层给用户一个Props来设置圆环进度条的大小。
理解path的d属性。 既然我们要使用path来画圆,那么我们其实需要熟悉一下d属性,它可以画各种直线。 d 属性用于定义路径数据。 我们先来了解一下我们需要用到的参数:
M=moveto(MX,Y):将笔画连接到指定坐标位置 A=ellipticalArc(ARX,RY,XROTATION,FLAG1,FLAG2,X,Y):椭圆弧
请注意⚠️,该参数对大小敏感。 当为小写命令时,表示其参数为绝对位置。 大写命令表示其擦除点是相对于当前位置的。 我们还可以使用正值作为命令的参数。 负的相对 x 值将向左移动,负的相对 y 值将向下移动。
让我们解释一下我们的两个参数
椭圆弧的参数方式:(rxryx-axis-rotationlarge-arc-flagsweep-flagxy) 参数解释:rxry为椭圆两个半轴的厚度。 x-axis-rotation 是椭圆相对于坐标系的旋转角度,以度而不是弧度为单位。 Large-arc-flag 标记是否勾画大弧 (1) 还是小弧 (0)。 scrap-flag 指示标记是按顺时针方向 (1) 还是逆时针方向 (0) 绘制。 xy 是圆弧终点的坐标。
由于我们需要让用户控制圆的起始绘制方向css3 进度条 渐变色,因此这里我们接受一个 props 来控制绘制方向。 根据上面的描述,我们可以写path的d属性,
在绝对位置50、50(圆心)处,先从圆心上方45的位置开始绘制(45为直径),然后绘制椭圆弧的参数,rx,ry,直径为45,旋转角度为0,轮廓为一个大圆弧,依次为轮廓方向、顺时针或逆二级旋转,最后是圆弧终点的坐标。如下代码可得
constpath=计算的(=>{
constisWise=props.顺时针?1:0;
返回`M5050m0-45a454501${isWise}090a454501,${isWise}0-90`;
});
本例中会绘制一个圆,使用的其他属性与上面的圆相同。
现在我们已经完成了路径的路径,下一步就是勾勒出圆圈,并正常添加动态进度变化。
首先,我们在顶部写下背景环。 用户可以自定义背景环弧线的颜色值和弧线长度。
/>
constpathStyle=计算的(=>{
返回{
描边:props.pathColor};
});
接下来是显示进度条的环。 由于这里我们还需要渐变颜色,因此我们还需要向 SVG 添加一些代码。
查阅SVG文档我们发现了一个名为 的SVG元素,通过使用这个元素我们可以达到颜色渐变的目的。
1. 创建线性渐变
在创建这个元素之前,我们需要知道标签必须嵌套在哪里。 标签是定义的缩写,可以定义渐变等特殊元素。 并且我们必须为渐变内容分配一个id属性,否则文档中的其他元素无法引用它。 为了使渐变可重用,渐变内容需要在标签内部定义,而不是在形状内部。
2.设置颜色渐变方向
现在元素创建成功了,我们可以利用它的形参属性来满足根据需要改变渐变颜色方向的需求。
渐变的方向可以通过两个点来控制,这两个点是属性x1、x2、y1和y2,它们定义了渐变的方向。 默认情况下,渐变是水平方向的,通过更改这些属性,可以旋转该方向。
3.设置渐变颜色
理论上添加的颜色没有上限,但如果有渐变效果,至少要添加两种颜色。 所以你需要在其中创建至少两个元素来添加你需要的颜色属性。
所以这里我们使用循环来处理多个颜色属性
元素具有三个属性:
conststopArray=计算的(=>{
if(!isObject(props.color)){
返回;
letcolor=props.color;
constcolorArr=Object.keys(color).sort((a,b)=>parseFloat(a)-parseFloat(b));
letstopArr:object[]=[];
colorArr.map((项目,索引)=>{
让obj={
钥匙: '',
价值: ''
};
obj.key=项目;
obj.value=颜色[项目];
停止Arr. 推(对象);
});
返回停止Arr;
});
添加完渐变色之后,我们来处理一下进度条(如何将进度条绑定到这个渐变色上)。 虽然很简单,只要让圆环的笔画成为唯一的id即可; 然后处理环的进度。 显示与上面圆的处理方法一致,可以使用strike-dasharray。
conthoverStyle=计算的(=>{
让周长=283;
letoffset=(周长*数量(props.progress))/100;
返回 {
描边:isObject(props.color)?`url(#${refRandomId})`:props.color,
strokeDasharray:`${偏移}px${周长}px`
};
});
上面的Stroke-linecap属性是一个representation属性,定义了遮罩时打开的子路径末尾要使用的形状css3 进度条 渐变色,可以在style中使用。
太郎下的 SVG
由于NutUI可以配合Taro来开发陌陌小程序,所以这里的圆形进度条在小程序环境下其实也有同样的功能。
由于我们在普通h5环境中使用的SVG是实现的,所以我们想应用一套代码,但是发现在小程序环境中不支持SVG的使用。
这个小程序官方文档里有解答,所以这里我用它作为背景图来显示。
我们通过一些将SVG转换为base64的网站发现只需将%3E;#替换为%23即可。 由于我们上面还有一些变量,所以我把它们拆开,写成几个变量。
conststyle=计算的(=>{
让{笔划宽度}=道具;
letstopArr:数组=停止;
letstopDom:string[]=[];
if(stopArr){
stopArr.map((项目:项目)=>{
让obj='';
obj=`%3Cstopoffset='${item.key}'stop-color='${transColor(item.value)}'/%3E`;
停止Dom。 推(对象);
});
让周长=283;
让progress=+currentRate.value;
letoffset=(周长*数字(格式(parseFloat(progress.toFixed(1)))))/100;
constisWise=props.顺时针?1:0;
constcolor=isObject(props.color)?`url(%23${refRandomId})`:transColor(props.color);
letd=`M5050m0-45a454501${isWise}090a454501,${isWise}0-90`;
constpa=`%3Cdefs%3E%3C LinearGradientx1='100%25'y1='0%25'x2='0%25'y2='0%25'%3E${stopDom}%3C/linearGradient%3E%3C /defs%3E`;
constpath=`%3Cpathd='${d}'笔画宽度='${笔画宽度}'笔画='${transColor(
props.pathColor)}'fill='none'/%3E`;
constpath1=`%3Cpathd='${d}'笔画宽度='${笔画宽度}'笔画-dasharray='${偏移},${周长}'笔画-linecap='圆形'笔画='${颜色}'
填充='无'/%3E`;
返回 {
背景:`url("数据:image/svg+xml,%3CsvgviewBox='00100100'xmlns='http://www.w3.org/2000/svg'%3E${pa}${path}${path1 }%3C/svg%3E")`,
宽度:'100%',
高度:'100%'
};
});
你认为这件事完成了吗? 不不不,你会发现,当你动态改变圆形进度条的进度时,没有动画,变得困难、僵化。
所以这里我们用它来减少一个动画的效果。 这里我们用setTimeout来代替requestAnimationFrame(不了解的朋友可以了解一下这个属性,非常有用!),因为小程序环境下不支持。
constrequestAnimationFrame=函数(回调:函数){
varcurrTime = 新日期。 获取时间;
vartimeToCall=Math.max(0,16.7-(currTime-lastTime));
最后时间=当前时间+通话时间;
变量=setTimeout(函数{
回调(currTime + timeToCall,lastTime);
}, 呼叫时间);
最后时间=当前时间+通话时间;
返回ID;
};
constcancelAnimationFrame=函数(id:任意){
清除超时(id);
};
然后我们就可以动态改变进度条了。
好了,我们的芋头适配现在就完成了。 我们来看看效果:
gif 图像可能不明显。 您可以在陌陌上搜索NutUI小程序来尝试一下。
结语
本文介绍了NutUI中circleProgress组件的设计思路和实现原理,鼓励大家这样做。 最后提一下我们的NutUI组件库。 多年来,团队成员一直在尽最大努力维护NutUI。 在未来的日子里,我们不会放弃这些坚持。 我们仍然会积极维护和迭代,为有需要的朋友提供技术支持,并且不定期发布一些相关文章,帮助您更好地了解和使用我们。 组件库。
快来给我们Star支持吧❤️~: