效果图
<template>
<canvas ref="canvasRef" :width="size" :height="size"></canvas>
</template>

<script setup lang="ts">
interface Props {
size: number;
targetProgress: number;
radius?: number;
lineWidth?: number;
bgColor?: string;
progressColor?: string;
textColor?: string;
}
const props = defineProps<Props>();
const canvasRef = ref<HTMLCanvasElement | null>(null);
let currentProgress = 0;
const drawProgress = (ctx: CanvasRenderingContext2D, progress: number) => {
const centerX = props.size / 2;
const centerY = props.size / 2;
const radius = props.radius || props.size / 2 - props.lineWidth!;
ctx.clearRect(0, 0, props.size, props.size);
// 背景圆环
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI);
ctx.lineWidth = props.lineWidth!;
ctx.strokeStyle = props.bgColor!;
ctx.stroke();
// 进度圆环
ctx.beginPath();
ctx.arc(centerX, centerY, radius, -Math.PI / 2, -Math.PI / 2 + 2 * Math.PI * (progress / 100));
ctx.lineWidth = props.lineWidth!;
ctx.strokeStyle = props.progressColor!;
ctx.lineCap = 'round'; // 设置线条末端为圆角
ctx.stroke();
// 进度文字
ctx.font = '30px Arial';
ctx.fillStyle = props.textColor!;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(progress.toFixed(0) + '%', centerX, centerY);
};
const animateProgress = () => {
if (!canvasRef.value) return;
const ctx = canvasRef.value.getContext('2d');
if (!ctx) return;
if (currentProgress < props.targetProgress) {
currentProgress += 1; // 调整这个值可以加快或减慢动画速度
drawProgress(ctx, currentProgress);
requestAnimationFrame(animateProgress);
} else {
drawProgress(ctx, props.targetProgress);
}
};
onMounted(() => {
animateProgress();
});
</script>
<style scoped>
canvas {
background-color: #444;
border-radius: 50%;
}
</style>