import { CSSProperties, FC, ReactNode, useEffect, useState } from 'react';
import classNames from 'classnames';
import './style.scss';
// strokeDasharray 的第一个值(填充部分长度)表示进度环的“实线”部分,应该有多长,而第二个值(圆周长)表示整个进度环的周长。
const getRingPercent = (percent: number, r: number) => {
const perimeter = Math.PI * 2 * r;
return (percent / 100) * perimeter + ' ' + perimeter;
};
export interface ProgressCircleProps {
className?: string;
style?: CSSProperties;
children?: ReactNode;
percent?: number;
// 圆环颜色
color?: string;
// 圆环底色
trackColor?: string;
// 圆环尺寸
size?: string | number;
// 圆环厚度
thickness?: number;
// 动画持续时间
dur?: number;
}
export const CircleProgress: FC<ProgressCircleProps> = (props) => {
const {
dur,
className,
style,
children,
percent = 0,
color,
trackColor,
size,
thickness = 4,
...restProps
} = props;
const [finalDashArray, setFinalDashArray] = useState('');
const radius = 50 - thickness / 2;
const perimeter = Math.PI * 2 * radius;
const progressClass = classNames('progress-circle', className);
const progressStyle = {
width: size,
height: size,
...style
};
const trackStyle = {
stroke: trackColor,
strokeWidth: thickness,
r: radius
};
const trailStyle = {
stroke: color,
strokeDasharray: finalDashArray,
strokeWidth: thickness,
r: radius
};
const animateFrom = `0 ${perimeter}`;
const animateTo = `${(percent / 100) * perimeter} ${perimeter}`;
useEffect(() => {
const finalDash = getRingPercent(percent, radius);
setFinalDashArray(finalDash);
}, [percent, radius]);
return (
<div {...restProps} className={progressClass} style={progressStyle}>
<svg viewBox='0 0 100 100' className='progress-circle-graph'>
<circle cx='50' cy='50' fill='none' className='progress-circle-track' style={trackStyle} />
<circle cx='50' cy='50' fill='none' className='progress-circle-trail' style={trailStyle}>
<animate
attributeName='stroke-dasharray'
begin='0s'
dur={dur + 's'} // 动画持续时间,例如1秒
from={animateFrom}
to={animateTo}
fill='freeze'
/>
</circle>
</svg>
{children}
</div>
);
};
export default CircleProgress;
样式:
.progress-circle {
position: relative;
z-index: 0;
display: flex;
justify-content: center;
align-items: center;
}
.progress-circle-graph {
position: absolute;
top: 0;
left: 0;
z-index: -1;
}
.progress-circle-trail {
stroke-linecap: round;
transform: rotate(-90deg);
transform-origin: center center;
transition: stroke-dasharray 3s ease,stroke .3s;
}
组件使用方法:
<CircleProgress
percent={40}
color='#4CAF50'
trackColor='#EEEEEE'
size='120px'
thickness={8}
dur={0.01}>
<h1>80分</h1>
</CircleProgress>