效果图
进度.jpg
Dom
import React, { FC, useMemo } from 'react'
import './index.less'
interface IProps {
// 进度条百分比
percent: number
// 渐进圆圈内部的内容
children?: any
// 自定义类名
className?: string
// 旋转方向(1为顺时针,-1为逆时针)
clockwise?: 1 | -1
/**
* @param {Object styleConfig 样式配置}
* @param {string} --size 画布大小配置
* @param {string} --track-width 环宽度配置
* @param {string} --track-color 环颜色配置
* @param {string} --fill-start-color 进度环开始颜色配置
* @param {string} --fill-end-color 进度环结束配置
* @param {string} --inner-circle-color 进度环内圆颜色配置
*/
styleConfig?: {
'--size'?: string
// 环宽度
'--track-width'?: string
// 环颜色
'--track-color'?: string
// 进度环开始颜色
'--fill-start-color'?: string
// 进度环结束颜色
'--fill-end-color'?: string
// 进度环内圆颜色
'--inner-circle-color'?: string
[key: string]: string | undefined
}
}
enum LIMIT_INTERVAL {
min = 0,
max = 100,
}
/**
* 渐进圆圈组件。
* @param {IProps} props - 组件属性。
* @param {ReactNode} props.children - 渐进圆圈内部的内容。
* @param {string} [props.className] - 自定义样式类名。
* @param {number} [props.percent=0] - 渐进百分比(0-100)。
* @param {number} [props.clockwise=1] - 旋转方向(1为顺时针,-1为逆时针)。
* @param {CSSProperties} [props.styleConfig] - 自定义样式配置。
* @returns {ReactElement} 渐进圆圈组件。
*/
const ProgressCircle: FC<IProps> = (props) => {
const { children, className, percent, clockwise = 1, styleConfig } =
props || {}
const isLimit = useMemo(() => {
return {
count: Number(percent > 100 ? 100 : percent < 0 ? 0 : percent || 0),
flag: [LIMIT_INTERVAL.min, LIMIT_INTERVAL.max].includes(
Number(percent || 0)
),
}
}, [percent])
return (
<>
<div className="progress-wrap">
<div
className={`progress-loop ${!isLimit.flag ? 'is-limit' : ''} ${
clockwise > 0 ? 'clockwise' : 'anticlockwise'
} ${className}`}
style={
{
'--progress': isLimit.count,
...(styleConfig || {}),
} as any
}
>
<div className="inner-circle"></div>
{isLimit.flag ? null : <div className="end"></div>}
</div>
<div className="progress-content">{children}</div>
</div>
</>
)
}
export default ProgressCircle
Css
.progress-wrap {
position: relative;
display: flex;
justify-content: center;
align-items: center;
--size: 120px;
// 环宽度
--track-width: 10px;
// 环颜色
--track-color: #ffe7d8;
// 进度环开始颜色
--fill-start-color: #ff873d;
// 进度环结束颜色
--fill-end-color: #ff873d;
// 进度环内圆颜色
--inner-circle-color: #fff;
// 进度值1-100
@progress: calc(var(--progress) * 1%);
.progress-content {
max-width: var(--inner-circle);
max-height: var(--inner-circle);
position: absolute;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-50%);
z-index: 2;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.progress-loop {
// 进度环内圆直径
--inner-circle: calc(var(--size) - var(--track-width) * 2);
position: relative;
width: var(--size);
height: $width;
background: #0a0a0a;
/* 进度条的颜色处理 */
background: conic-gradient(
var(--fill-start-color),
var(--fill-end-color) @progress,
var(--track-color) @progress,
var(--track-color)
);
border-radius: 50%;
position: relative;
transform: rotateY(0);
// 逆时针渲染
&.anticlockwise {
transform: rotateY(180deg);
}
// 顺时针渲染
&.clockwise {
transform: rotateY(0deg);
}
/* 起点的弧度 */
&.is-limit::before {
position: absolute;
display: inline-block;
content: '';
width: var(--track-width);
height: var(--track-width);
border-radius: 50%;
background-color: var(--fill-start-color);
top: 0;
left: 50%;
transform: translateX(-50%);
}
// 终点圆弧
.end {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 50%;
/* 这个旋转的角度 是 360*progress(百分比 /100) */
/* 例子 360 * 0.7 = 252 */
transform: rotate(calc(var(--progress) / 100 * 360deg));
/* 终点的弧度 */
&::before {
position: absolute;
display: inline-block;
content: '';
width: var(--track-width);
height: var(--track-width);
border-radius: 50%;
background-color: var(--fill-end-color);
top: 0;
left: 50%;
transform: translateX(-50%);
}
}
// 内圆
.inner-circle {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: var(--inner-circle);
height: var(--inner-circle);
background-color: var(--inner-circle-color);
border-radius: 50%;
}
}
}