背景
在业务需求中,遇到了上述的开发需求环形进度条,如上所示,在实际开发中,环形进度条也比较常见。使用组件库的会偏多。
本来打算用border-radius实现,会有两个问题
1.圆圈的切口是圆状的
2.不是整个圆
就不是很合适。
思路
然后搜了一下,发现了svg标签中的circle
svg标签中还有其他的图形,svg标签,几种图形基本使用
step1: 先一个创建svg画布,绘制一个简单的圆形。作为环形进度条的基础。
step2: 两个圆圈 ,打底的圆圈,外边的渐变颜色
step3: 利用circle属性
strokeLinecap="round" // 切面为圆
strokeDasharray="400,134" //实现空白
strokeDashoffset="-134" //偏移位置缺口
实现
<svg width="174" height="174" className="circle-progress">
<circle
cx="87" // 距离父亲的横向坐标系
cy="87" // 距离父亲的纵向坐标系
r="85" // 半径
fill="none" // 填充
strokeLinecap="round" //切面为圆
strokeWidth="4" // 边的宽度
stroke="#F4E8DC" // 颜色
strokeDasharray="400,134"
strokeDashoffset="-134" //偏移的角度
/>
<circle
className={clsx(
`progress-${progressNum.split('%')[0]}`,
)}
cx="87"
cy="87"
r="85"
fill="none"
strokeLinecap="round"
strokeWidth="4"
strokeDasharray="400,133"
strokeDashoffset="-134" //偏移的角度
/>
</svg>
storke-dasharray
cx,cy,r
由于是固定几个百分比,又不知道怎么向css传参,又想有动效,然后我的代码就每写一个百分比就写对应百分比的动效className
.circle-progress-wrap {
margin: auto;
width: 245px;
margin-top: 24px;
height: 245px;
position: relative;
background: url('~@/assets/img/progress_bg@2x.png') no-repeat top/contain;
.circle-progress-num {
position: absolute;
top: 78px;
width: 100%;
margin: auto;
.circle-progress-label {
color: var(--hll-color-black-40);
}
.circle-progress-value {
font-weight: 700;
color: var(--hll-color-black-90);
margin-top: 4px;
line-height: 53px;
height: 53px;
font-size: 40px;
}
}
.circle-progress {
margin-top: 35px;
transform: rotate(44deg);
}
.progress {
animation: circleProgress 2s;
animation-iteration-count: 1;
animation-fill-mode: forwards;
}
@keyframes circleProgress {
0% {
stroke-dashoffset: 500;
stroke: #ffe19d;
}
100% {
stroke-dashoffset: 150;
stroke: #c4955b;
}
}
.progress-33 {
animation: circleProgress33 2s;
animation-iteration-count: 1;
animation-fill-mode: forwards;
}
@keyframes circleProgress33 {
0% {
stroke-dasharray: 0, 400;
stroke: #ffe19d;
}
100% {
stroke-dasharray: 132, 268;
stroke: #c4955b;
}
}
.progress-50 {
animation: circleProgress50 2s;
animation-iteration-count: 1;
animation-fill-mode: forwards;
}
@keyframes circleProgress50 {
0% {
stroke-dasharray: 0, 534;
stroke: #ffe19d;
}
100% {
// stroke-dashoffset: 340;
stroke-dasharray: 200, 334;
stroke: #c4955b;
}
}
最终章
然后我想不会吧。组件库里边也是我这样?
mobile.ant.design/zh/componen…
antd-mobile circleProgress
看高亮部分。其他的可不看。
import React, { CSSProperties, FC } from 'react'
const classPrefix = `adm-progress-circle`
export type ProgressCircleProps = {
percent?: number
children?: React.ReactNode
} & NativeProps<'--size' | '--track-width' | '--track-color' | '--fill-color'>
export const ProgressCircle: FC<ProgressCircleProps> = p => {
const props = mergeProps({ percent: 0 }, p)
** const style: CSSProperties & Record<'--percent', string> = {
'--percent': props.percent.toString(),
} //这个是重点**
return
<div className={`${classPrefix}`} style={style}>
<div className={`${classPrefix}-content`}>
<svg className={`${classPrefix}-svg`}>
<circle className={`${classPrefix}-track`} fill='transparent' />
<circle className={`${classPrefix}-fill`} fill='transparent' />
</svg>
<div className={`${classPrefix}-info`}>{props.children}</div>
</div>
</div>
)
}
@class-prefix-progress-circle: ~'adm-progress-circle';
/*进度圈*/
.@{class-prefix-progress-circle} {
--track-width: var(--adm-progress-circle-track-width, 3px); //支持传参使用,默认xxx
--size: var(--adm-progress-circle-size, 50px);
--track-color: var(--adm-progress-circle-track-color, #e5e5e5);
--fill-color: var(--adm-progress-circle-fill-color, var(--adm-color-primary));
--percent: 0;
--pi: 3.14159265358979;
--radius: calc(var(--size) / 2 - var(--track-width) / 2);
--circumference: calc(var(--radius) * var(--pi) * 2);
display: inline-block;
width: var(--size);
height: var(--size);
&-svg {
width: 100%;
height: 100%;
> .@{class-prefix-progress-circle}-track,
.@{class-prefix-progress-circle}-fill {
stroke-width: var(--track-width);
r: var(--radius);
cx: calc(var(--size) / 2);
cy: calc(var(--size) / 2);
transform: rotate(-90deg);
transform-origin: 50% 50%;
}
> .@{class-prefix-progress-circle}-track {
stroke: var(--track-color);
}
> .@{class-prefix-progress-circle}-fill {
transition: stroke-dashoffset 0.35s;//动效用得transition
stroke: var(--fill-color);
stroke-dasharray: var(--circumference);
** stroke-dashoffset: calc(
var(--circumference) * (1 - var(--percent) / 100)
); //这个--percent是从style的属性传参进来的**
stroke-linecap: round;
}
}
&-content {
position: relative;
margin: auto;
width: 100%;
height: 100%;
}
&-info {
position: absolute;
width: 100%;
top: 50%;
left: 50%;
text-align: center;
transform: translate(-50%, -50%);
}
}
发现直接通过传入的percent,然后传入css属性,通过calc来算的。优秀了
后续
然后敲了几个 demo ,放在了codesandbox上了,进行对比。demo1,是我自己的初版,demo2,是antd-mobile的源码,demo3是最终版
存在问题
calc不太聪明的样子,有些组合运算算不明白?
颜色渐变,可能还需要通过进度具体数值计算,然后渐变。 这个渐变的,我现在解决啦。
<circle
className={`${classPrefix}-fill`}
stroke="url(#linearGradient)" //这里是重点
fill="transparent"
/>
<defs>
<linearGradient
x1="16%"
y1="30.9878035%"
x2="96.9769965%"
y2="100%"
id="linearGradient" //这里是重点
>
<stop stopColor="#c4955b" offset="0%"></stop>
<stop stopColor="#c4955b" offset="52.7095376%"></stop>
<stop stopColor="#ffe19d" offset="100%"></stop>
</linearGradient>
</defs>
最终的效果就