如何使用React与React Native实现自定义圆形进度条
你现有的两个坐标转换、弧线路径生成函数已经覆盖了核心计算逻辑,直接基于这两个函数封装组件即可,不需要依赖第三方包,适配Web和React Native的方案如下:
React Web 实现(基于原生SVG)
核心是用两层path分别做背景轨道和进度条,绑定props动态计算进度对应的弧长:
import React from 'react'; // 复用你已有的工具函数 function polarToCartesian(centerX, centerY, radius, angleInDegrees) { const angleInRadians = ((angleInDegrees - 90) * Math.PI) / 180.0; return { x: centerX + radius * Math.cos(angleInRadians), y: centerY + radius * Math.sin(angleInRadians), }; } function describeArc(x, y, radius, startAngle, endAngle) { const start = polarToCartesian(x, y, radius, endAngle); const end = polarToCartesian(x, y, radius, startAngle); const largeArcFlag = endAngle - startAngle <= 180 ? '0' : '1'; const d = [ 'M', start.x, start.y, 'A', radius, radius, 0, largeArcFlag, 0, end.x, end.y ].join(' '); return d; } // 圆形进度条组件 const CircleProgress = ({ progress = 0, size = 200, strokeWidth = 12, bgColor = '#e5e7eb', progressColor = '#3b82f6', showText = true }) => { const radius = (size - strokeWidth) / 2; const center = size / 2; // 进度转角度 0-100对应0-360度 const angle = Math.min(360, Math.max(0, (progress / 100) * 360)); const progressPath = angle === 0 ? '' : describeArc(center, center, radius, 0, angle); return ( <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}> {/* 背景轨道 */} <path d={describeArc(center, center, radius, 0, 360)} fill="none" stroke={bgColor} strokeWidth={strokeWidth} strokeLinecap="round" /> {/* 进度条 */} {angle > 0 && ( <path d={progressPath} fill="none" stroke={progressColor} strokeWidth={strokeWidth} strokeLinecap="round" /> )} {/* 中间进度文字 */} {showText && ( <text x={center} y={center} textAnchor="middle" dominantBaseline="middle" fontSize={size * 0.2} fontWeight="bold" fill={progressColor}> {Math.round(progress)}% </text> )} </svg> ); }; export default CircleProgress;
调用时直接传参数即可:<CircleProgress progress={75} />
React Native 实现
核心逻辑和Web完全一致,只需要把SVG标签替换为react-native-svg提供的对应组件即可:
import React from 'react'; import { View, Text } from 'react-native'; import Svg, { Path } from 'react-native-svg'; // 复用你已有的两个工具函数,完全不需要修改 function polarToCartesian(centerX, centerY, radius, angleInDegrees) { const angleInRadians = ((angleInDegrees - 90) * Math.PI) / 180.0; return { x: centerX + radius * Math.cos(angleInRadians), y: centerY + radius * Math.sin(angleInRadians), }; } function describeArc(x, y, radius, startAngle, endAngle) { const start = polarToCartesian(x, y, radius, endAngle); const end = polarToCartesian(x, y, radius, startAngle); const largeArcFlag = endAngle - startAngle <= 180 ? '0' : '1'; const d = [ 'M', start.x, start.y, 'A', radius, radius, 0, largeArcFlag, 0, end.x, end.y ].join(' '); return d; } const CircleProgress = ({ progress = 0, size = 200, strokeWidth = 12, bgColor = '#e5e7eb', progressColor = '#3b82f6', showText = true }) => { const radius = (size - strokeWidth) / 2; const center = size / 2; const angle = Math.min(360, Math.max(0, (progress / 100) * 360)); const progressPath = angle === 0 ? '' : describeArc(center, center, radius, 0, angle); return ( <View style={{ width: size, height: size, position: 'relative' }}> <Svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}> <Path d={describeArc(center, center, radius, 0, 360)} fill="none" stroke={bgColor} strokeWidth={strokeWidth} strokeLinecap="round" /> {angle > 0 && ( <Path d={progressPath} fill="none" stroke={progressColor} strokeWidth={strokeWidth} strokeLinecap="round" /> )} </Svg> {showText && ( <Text style={{ position: 'absolute', top: 0, left: 0, width: size, height: size, textAlign: 'center', textAlignVertical: 'center', fontSize: size * 0.2, fontWeight: 'bold', color: progressColor }} > {Math.round(progress)}% </Text> )} </View> ); }; export default CircleProgress;
如果需要实现非整圆的缺口式圆形进度条,只需要调整describeArc传入的起始角度和最大角度即可,比如270度的仪表盘样式,设置起始角度为135,最大角度为135+270=405,进度对应角度按比例映射到135到405的区间即可。如果需要添加动画效果,直接绑定动画变量到progress属性即可。
内容的提问来源于stack exchange,提问作者AirFlight
相关产品推荐
相关产品推荐

