如何在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;
关键改动说明:
移除SVG旋转,改用偏移量控制进度起点:
去掉了原SVG的transform='rotate(-90)',改用strokeDashoffset = -CIRC * 0.25让进度条从顶部开始,避免旋转影响内嵌元素的渐变方向。添加SVG遮罩:
创建mask元素定义进度段的环形形状(带圆角端点的虚线圆),遮罩中白色区域会显示内容,透明/黑色区域会隐藏内容。用foreignObject内嵌渐变元素:
通过foreignObject在SVG中插入一个普通div,给div设置锥形渐变背景,再通过mask="url(#progressMask)"让div只显示遮罩定义的进度区域。优化文本居中:
给外层Box添加flex布局实现居中,文本设置为绝对定位确保悬浮在进度条上方。
内容的提问来源于stack exchange,提问作者entithat
相关产品推荐
相关产品推荐

