canvas 半圆环

56 阅读1分钟
import React, { useEffect, useRef } from 'react';

interface ProgressRingProps {
  progress?: number; // 进度值 0-1
  radius?: number;   // 圆环半径
  lineWidth?: number; // 圆环宽度
  bgColor?: string;  // 背景颜色
  progressColor?: string; // 进度条颜色
}

const ProgressRing: React.FC<ProgressRingProps> = ({
  progress = 0.8,
  radius = 25,
  lineWidth = 6,
  bgColor = 'rgba(255, 255, 255, 0.3)',
  progressColor = 'rgba(255, 255, 255, 0.4)',
}) => {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const size = 66;  // 固定画布大小为66px

  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;

    const ctx = canvas.getContext('2d');
    if (!ctx) return;

    // 获取设备像素比
    const dpr = window.devicePixelRatio || 1;

    // 设置canvas实际绘制尺寸(乘以像素比)
    canvas.width = size * dpr;
    canvas.height = size * dpr;

    // 缩放画布上下文
    ctx.scale(dpr, dpr);

    // 设置canvas的CSS尺寸保持原显示大小
    canvas.style.width = `${size}px`;
    canvas.style.height = `${size}px`;

    // 清除画布
    ctx.clearRect(0, 0, size, size);

    const centerX = size / 2;
    const centerY = size / 2;

    // 绘制背景圆环(270度,开口90度)
    ctx.beginPath();
    // 从-45度开始到225度结束(总共270度)
    ctx.arc(centerX, centerY, radius, -Math.PI / 4, (5 * Math.PI) / 4, false);
    ctx.lineWidth = lineWidth;
    ctx.strokeStyle = bgColor;
    ctx.lineCap = 'round';
    ctx.stroke();

    if (progress > 0) {
      // 绘制进度圆环
      const startAngle = -Math.PI / 4; // 从-45度开始
      const endAngle = startAngle + ((3 * Math.PI) / 2 * Math.min(progress, 1)); // 计算270度范围内的进度

      ctx.beginPath();
      ctx.arc(centerX, centerY, radius, startAngle, endAngle, false);
      ctx.lineWidth = lineWidth;
      ctx.strokeStyle = progressColor;
      ctx.lineCap = 'round';
      ctx.stroke();
    }
  }, [progress, radius, lineWidth, bgColor, progressColor, size]);

  return (
    <canvas
      ref={canvasRef}
      style={{ display: 'block' }}
    />
  );
};

export default ProgressRing;
`