canvas实现 环形进度条 封装组件 vue3+ts

25 阅读1分钟

效果图

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

![image.png](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/0dfe7a0d263d4a60bdf8d06df4e475dd~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5LiA5aSpZWU=:q75.awebp?rk3s=f64ab15b&x-expires=1743764251&x-signature=0XJFDfxORNeK%2Bkx%2BfInd1CIOwsQ%3D)
<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>