SvgCirPro 使用svg实现圆环进度条 支持渐变色和动画
import React, { Component } from "react"; import PropTypes from "prop-types"; export default class SvgCirPro extends Component { constructor(props) { super(props); this.state = { percent: 0, }; } static defaultProps = { offset: 3, radius: 45, percent: 15, borderWidth: 6, startcolor: '#ffd460', centercolor: '#fcc241', endColor: '#f79d00', textStyle: { fontSize: 20, color: "#fa9a22", textAlign: 'center' }, openAnimation: true, } static propTypes = { offset: PropTypes.number, radius: PropTypes.number, percent: PropTypes.number, borderWidth: PropTypes.number, startcolor: PropTypes.string, centercolor: PropTypes.string, endColor: PropTypes.string, textStyle: PropTypes.object, openAnimation: PropTypes.bool, } /** * 传入相应参数返回圆形制定半径的弧度坐标 * @param {*} x 中心点X坐标 * @param {*} y 中心点y坐标 * @param {*} R 圆半径 * @param {*} a 角度 */ coordMap(x, y, R, a) { var ta = (360 - a) * Math.PI / 180, tx, ty; tx = R * Math.cos(ta); // 角度邻边 ty = R * Math.sin(ta); // 角度的对边 return { x: x + tx, y: y - ty // 注意此处是“-”号,因为我们要得到的Y是相对于(0,0)而言的。 } } render() { let endAngle = 3.6 * (this.props.openAnimation ? this.state.percent : this.props.percent); let radius = this.props.radius; let width = (this.props.radius + this.props.borderWidth) * 2; let height = (this.props.radius + this.props.borderWidth) * 2; let startPoint1, endPoint1, endPoint2; if (endAngle <= 180) { startPoint1 = this.coordMap(width / 2, height / 2, radius, 0 - 120); endPoint1 = this.coordMap(width / 2, height / 2, radius, endAngle - 120); } else { startPoint1 = this.coordMap(width / 2, height / 2, radius, 0 - 120); endPoint1 = this.coordMap(width / 2, height / 2, radius, 180 - 120); endPoint2 = this.coordMap(width / 2, height / 2, radius, endAngle - 130); } return (<div className="svgcirproOut" style={{ width: width, height: height, }} > <svg className="svgcirproIn" ref={svgcirpro => { }} height={height} version="1.1" width={width} xmlns="http://www.w3.org/2000/svg" > <defs> <linearGradient id={"lgrad1" + this.props.startcolor} x1="0" y1="0" x2="0" y2="100%" > <stop offset="0%" style={{ stopColor: this.props.startcolor, stopOpacity: 1 }} /> <stop offset="100%" style={{ stopColor: this.props.centercolor, stopOpacity: 1 }} /> </linearGradient> <linearGradient id={"lgrad2" + this.props.startcolor} x1="0" y1="100%" x2="0" y2="0" > <stop offset="0%" style={{ stopColor: this.props.centercolor, stopOpacity: 1 }} /> <stop offset="100%" style={{ stopColor: this.props.endColor, stopOpacity: 1 }} /> </linearGradient> </defs> <path d={"M" + startPoint1.x + "," + startPoint1.y + "A" + radius + "," + radius + ",0,0,1," + endPoint1.x + "," + endPoint1.y} stroke={"url(#lgrad1" + this.props.startcolor + ")"} fill="none" strokeLinecap="round" strokeWidth={this.props.borderWidth} /> {endAngle > 180 ? <path d={"M" + endPoint1.x + "," + endPoint1.y + "A" + radius + "," + radius + ",0,0,1," + endPoint2.x + "," + endPoint2.y} stroke={"url(#lgrad2" + this.props.startcolor + ")"} fill="none" strokeLinecap="round" strokeWidth={this.props.borderWidth} /> : null } </svg> <div className="svgcirproContent" style={{ width: width, height: height, marginTop: -(height+this.props.offset) }} > {this.props.children ? this.props.children : <div style={this.props.textStyle}>{this.props.openAnimation ? this.state.percent : this.props.percent}%</div>} </div> </div>); } componentWillReceiveProps(newprops) { if (this.props.openAnimation) { this.setState({ percent: 0 }); this.time = setTimeout(() => { this.componentDidMount(); }, 1); } else { this.setState({ percent: newprops.percent }); } } componentDidMount() { if (this.props.openAnimation) { this.startAnimation(); } } startAnimation() { if (this.state.percent < this.props.percent - 2) { this.time = setTimeout(() => { this.setState({ percent: this.state.percent + 2 }); this.startAnimation(); }, 11); } else { this.setState({ percent: this.props.percent }); } } onDraw(svgcirpro) { if (svgcirpro) { } } } /** * Arc(A rx,ry,xrotation,flag1,flag2,x,y) * rx.ry 椭圆的半轴大小 * xrotation 椭圆的x轴与水平方向顺时针的夹角 * flag1只有两个值 1大角度 0小角度 是否大于180 * flag2只有两个值 1为顺时针 0为逆时针 * x y 为终点坐标 * * * 100:per1=per2:100 */
.svgcirproOut { overflow: hidden; } .svgcirproContent { display: flex; display: -webkit-flex; justify-content: center; -webkit-justify-content: center; align-items: center; -webkit-align-items: center; }
qq交流群
179916551 欢迎进群交流前端各种问题
github
https://github.com/1cao2mu/react-circle-progress 如果只是参考思路,也请给个star 谢谢