You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在React的SVG环形进度条中添加conic gradient

实现带锥形渐变的环形进度条

由于SVG本身不支持锥形渐变,我们可以通过SVG遮罩(mask)+ 内嵌CSS渐变元素的方式实现。核心思路是用SVG定义进度条的环形形状作为遮罩,再将带有锥形渐变的普通元素通过遮罩显示出对应的进度区域。

以下是修改后的React组件代码:

import React from 'react';
import { Box, Text } from '@chakra-ui/react';

const GradientProgress = ({ modifier, score, size, strokeWidth }) => {
  const DIAMETER = 51;
  const WIDTH = DIAMETER + strokeWidth;

  const RADIUS = DIAMETER / 2;
  const CIRC = 2 * Math.PI * RADIUS;

  const foregroundCirc = (CIRC * score) / 100;
  const frontCirc = (CIRC * modifier) / 100;
  const strokeOffset = -CIRC * 0.25; // 让进度条从顶部开始

  return (
    <Box
      position='relative'
      style={{ width: `${size}px`, height: `${size}px` }}
      display="flex"
      alignItems="center"
      justifyContent="center"
    >
      <svg
        className='donut'
        viewBox={`0 0 ${WIDTH} ${WIDTH}`}
        style={{ width: '100%', height: '100%' }}
      >
        {/* 定义进度条形状的遮罩 */}
        <mask id="progressMask">
          <circle
            cx={RADIUS + strokeWidth / 2}
            cy={RADIUS + strokeWidth / 2}
            fill="transparent"
            r={RADIUS}
            stroke="white"
            strokeDasharray={`${foregroundCirc} ${CIRC - foregroundCirc}`}
            strokeDashoffset={strokeOffset}
            strokeLinecap="round"
            strokeWidth={strokeWidth}
          />
        </mask>

        {/* 灰色背景环 */}
        <circle
          className='donut-ring'
          cx={RADIUS + strokeWidth / 2}
          cy={RADIUS + strokeWidth / 2}
          fill='transparent'
          r={RADIUS}
          stroke='#d2d3d4'
          strokeWidth={strokeWidth}
        />

        {/* 半透明的modifier进度段 */}
        <circle
          className='donut-segment'
          cx={RADIUS + strokeWidth / 2}
          cy={RADIUS + strokeWidth / 2}
          fill='transparent'
          opacity={0.5}
          r={RADIUS}
          stroke='green'
          strokeDasharray={`${frontCirc} ${CIRC - frontCirc}`}
          strokeDashoffset={strokeOffset}
          strokeLinecap='round'
          strokeWidth={strokeWidth}
        />

        {/* 带锥形渐变的进度段:用foreignObject内嵌div,通过遮罩显示 */}
        <foreignObject
          x="0"
          y="0"
          width={WIDTH}
          height={WIDTH}
          mask="url(#progressMask)"
        >
          <div
            xmlns="http://www.w3.org/1999/xhtml"
            style={{
              width: '100%',
              height: '100%',
              background: 'conic-gradient(from 90deg, #ff4800 10%, #dfd902 35%, #20dc68, #0092f4, #da54d8 72% 75%, #ff4800 95%)',
            }}
          />
        </foreignObject>
      </svg>
      <Text position="absolute">{modifier || score}</Text>
    </Box>
  );
};

export default GradientProgress;

关键改动说明:

  1. 移除SVG旋转,改用偏移量控制进度起点:
    去掉了原SVG的transform='rotate(-90)',改用strokeDashoffset = -CIRC * 0.25让进度条从顶部开始,避免旋转影响内嵌元素的渐变方向。

  2. 添加SVG遮罩:
    创建mask元素定义进度段的环形形状(带圆角端点的虚线圆),遮罩中白色区域会显示内容,透明/黑色区域会隐藏内容。

  3. 用foreignObject内嵌渐变元素:
    通过foreignObject在SVG中插入一个普通div,给div设置锥形渐变背景,再通过mask="url(#progressMask)"让div只显示遮罩定义的进度区域。

  4. 优化文本居中:
    给外层Box添加flex布局实现居中,文本设置为绝对定位确保悬浮在进度条上方。

内容的提问来源于stack exchange,提问作者entithat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 23:36:23