怎么用react native实现圆弧拖动进度条(native,react,移动开发)

时间:2024-05-08 14:15:57 作者 : 石家庄SEO 分类 : 移动开发
  • TAG :

先上效果图

怎么用react native实现圆弧拖动进度条

因为需求需要实现这个效果图 非原生实现,

  1. 难点1:绘制 使用svg

  2. 难点2:点击事件的处理

  3. 难点3:封装

由于绘制需要是使用svg

此处自行百度 按照svg以及api 教学

视图代码块

render(){return(<ViewpointerEvents={'box-only'}//事件处理{...this._panResponder.panHandlers}>//实际圆环{this._renderCircleSvg()}//计算中心距离<Viewstyle={{position:'relative',top:-this.props.height/2-this.props.r,left:this.props.width/2-this.props.r,flex:1,}}>//暴露给外部渲染圆环中心的接口{this.props.renderCenterView(this.state.temp)}</View></View>);_renderCircleSvg(){//中心点constcx=this.props.width/2;constcy=this.props.height/2;//计算是否有偏差角对应图就是下面缺了一块的constprad=this.props.angle/2*(Math.PI/180);//三角计算起点conststartX=-(Math.sin(prad)*this.props.r)+cx;conststartY=cy+Math.cos(prad)*this.props.r;//终点constendX=Math.sin(prad)*this.props.r+cx;constendY=cy+Math.cos(prad)*this.props.r;//计算进度点constprogress=parseInt(this._circlerate()*(360-this.props.angle)/100,10);//根据象限做处理苦苦苦高中数学全忘了,参考辅助线constt=progress+this.props.angle/2;constprogressX=cx-Math.sin(t*(Math.PI/180))*this.props.r;constprogressY=cy+Math.cos(t*(Math.PI/180))*this.props.r;//SVG的描述这里百度下就知道什么意思constdescriptions=['M',startX,startY,'A',this.props.r,this.props.r,0,1,1,endX,endY,].join('');constprogressdescription=['M',startX,startY,'A',this.props.r,this.props.r,0,//根据角度是否是0,1看下效果就知道了t>=180+this.props.angle/2?1:0,1,progressX,progressY,].join('');return(<Svgheight={this.props.height}width={this.props.width}style={styles.svg}><Pathd={descriptions}fill="none"stroke={this.props.outArcColor}strokeWidth={this.props.strokeWidth}/><Pathd={progressdescription}fill="none"stroke={this.props.progressvalue}strokeWidth={this.props.strokeWidth}/><Circlecx={progressX}cy={progressY}r={this.props.tabR}stroke={this.props.tabStrokeColor}strokeWidth={this.props.tabStrokeWidth}fill={this.props.tabColor}/></Svg>);}}

事件处理代码块

//参考reactnative官网对手势的讲解iniPanResponder(){this.parseToDeg=this.parseToDeg.bind(this);this._panResponder=PanResponder.create({//要求成为响应者:onStartShouldSetPanResponder:()=>true,onStartShouldSetPanResponderCapture:()=>true,onMoveShouldSetPanResponder:()=>true,onMoveShouldSetPanResponderCapture:()=>true,onPanResponderGrant:evt=>{//开始手势操作。给用户一些视觉反馈,让他们知道发生了什么事情!if(this.props.enTouch){this.lastTemper=this.state.temp;constx=evt.nativeEvent.locationX;consty=evt.nativeEvent.locationY;this.parseToDeg(x,y);}},onPanResponderMove:(evt,gestureState)=>{if(this.props.enTouch){letx=evt.nativeEvent.locationX;lety=evt.nativeEvent.locationY;if(Platform.OS==='android'){x=evt.nativeEvent.locationX+gestureState.dx;y=evt.nativeEvent.locationY+gestureState.dy;}this.parseToDeg(x,y);}},onPanResponderTerminationRequest:()=>true,onPanResponderRelease:()=>{if(this.props.enTouch)this.props.complete(this.state.temp);},//另一个组件已经成为了新的响应者,所以当前手势将被取消。onPanResponderTerminate:()=>{},//返回一个布尔值,决定当前组件是否应该阻止原生组件成为JS响应者//默认返回true。目前暂时只支持android。onShouldBlockNativeResponder:()=>true,});}//画象限看看就知道了就是和中线点计算角度parseToDeg(x,y){constcx=this.props.width/2;constcy=this.props.height/2;letdeg;lettemp;if(x>=cx&&y<=cy){deg=Math.atan((cy-y)/(x-cx))*180/Math.PI;temp=(270-deg-this.props.angle/2)/(360-this.props.angle)*(this.props.max-this.props.min)+this.props.min;}elseif(x>=cx&&y>=cy){deg=Math.atan((cy-y)/(cx-x))*180/Math.PI;temp=(270+deg-this.props.angle/2)/(360-this.props.angle)*(this.props.max-this.props.min)+this.props.min;}elseif(x<=cx&&y<=cy){deg=Math.atan((x-cx)/(y-cy))*180/Math.PI;temp=(180-this.props.angle/2-deg)/(360-this.props.angle)*(this.props.max-this.props.min)+this.props.min;}elseif(x<=cx&&y>=cy){deg=Math.atan((cx-x)/(y-cy))*180/Math.PI;if(deg<this.props.angle/2){deg=this.props.angle/2;}temp=(deg-this.props.angle/2)/(360-this.props.angle)*(this.props.max-this.props.min)+this.props.min;}if(temp<=this.props.min){temp=this.props.min;}if(temp>=this.props.max){temp=this.props.max;}//因为提供步长,所欲需要做接近步长的数temp=this.getTemps(temp);this.setState({temp,});this.props.valueChange(this.state.temp);}getTemps(tmps){constk=parseInt((tmps-this.props.min)/this.props.step,10);constk1=this.props.min+this.props.step*k;constk2=this.props.min+this.props.step*(k+1);if(Math.abs(k1-tmps)>Math.abs(k2-tmps))returnk2;returnk1;}

完整代码块

importReact,{Component}from'react';import{View,StyleSheet,PanResponder,Platform,Text}from'react-native';importSvg,{Circle,Path}from'react-native-svg';exportdefaultclassCircleViewextendsComponent{staticpropTypes={height:React.PropTypes.number,width:React.PropTypes.number,r:React.PropTypes.number,angle:React.PropTypes.number,outArcColor:React.PropTypes.object,progressvalue:React.PropTypes.object,tabColor:React.PropTypes.object,tabStrokeColor:React.PropTypes.object,strokeWidth:React.PropTypes.number,value:React.PropTypes.number,min:React.PropTypes.number,max:React.PropTypes.number,tabR:React.PropTypes.number,step:React.PropTypes.number,tabStrokeWidth:React.PropTypes.number,valueChange:React.PropTypes.func,renderCenterView:React.PropTypes.func,complete:React.PropTypes.func,enTouch:React.PropTypes.boolean,};staticdefaultProps={width:300,height:300,r:100,angle:60,outArcColor:'white',strokeWidth:10,value:20,min:10,max:70,progressvalue:'#ED8D1B',tabR:15,tabColor:'#EFE526',tabStrokeWidth:5,tabStrokeColor:'#86BA38',valueChange:()=>{},complete:()=>{},renderCenterView:()=>{},step:1,enTouch:true,};constructor(props){super(props);this.state={temp:this.props.value,};this.iniPanResponder();}iniPanResponder(){this.parseToDeg=this.parseToDeg.bind(this);this._panResponder=PanResponder.create({//要求成为响应者:onStartShouldSetPanResponder:()=>true,onStartShouldSetPanResponderCapture:()=>true,onMoveShouldSetPanResponder:()=>true,onMoveShouldSetPanResponderCapture:()=>true,onPanResponderGrant:evt=>{//开始手势操作。给用户一些视觉反馈,让他们知道发生了什么事情!if(this.props.enTouch){this.lastTemper=this.state.temp;constx=evt.nativeEvent.locationX;consty=evt.nativeEvent.locationY;this.parseToDeg(x,y);}},onPanResponderMove:(evt,gestureState)=>{if(this.props.enTouch){letx=evt.nativeEvent.locationX;lety=evt.nativeEvent.locationY;if(Platform.OS==='android'){x=evt.nativeEvent.locationX+gestureState.dx;y=evt.nativeEvent.locationY+gestureState.dy;}this.parseToDeg(x,y);}},onPanResponderTerminationRequest:()=>true,onPanResponderRelease:()=>{if(this.props.enTouch)this.props.complete(this.state.temp);},//另一个组件已经成为了新的响应者,所以当前手势将被取消。onPanResponderTerminate:()=>{},//返回一个布尔值,决定当前组件是否应该阻止原生组件成为JS响应者//默认返回true。目前暂时只支持android。onShouldBlockNativeResponder:()=>true,});}componentWillReceiveProps(nextProps){if(nextProps.value!=this.state.temp){this.state={temp:nextProps.value,};}}parseToDeg(x,y){constcx=this.props.width/2;constcy=this.props.height/2;letdeg;lettemp;if(x>=cx&&y<=cy){deg=Math.atan((cy-y)/(x-cx))*180/Math.PI;temp=(270-deg-this.props.angle/2)/(360-this.props.angle)*(this.props.max-this.props.min)+this.props.min;}elseif(x>=cx&&y>=cy){deg=Math.atan((cy-y)/(cx-x))*180/Math.PI;temp=(270+deg-this.props.angle/2)/(360-this.props.angle)*(this.props.max-this.props.min)+this.props.min;}elseif(x<=cx&&y<=cy){deg=Math.atan((x-cx)/(y-cy))*180/Math.PI;temp=(180-this.props.angle/2-deg)/(360-this.props.angle)*(this.props.max-this.props.min)+this.props.min;}elseif(x<=cx&&y>=cy){deg=Math.atan((cx-x)/(y-cy))*180/Math.PI;if(deg<this.props.angle/2){deg=this.props.angle/2;}temp=(deg-this.props.angle/2)/(360-this.props.angle)*(this.props.max-this.props.min)+this.props.min;}if(temp<=this.props.min){temp=this.props.min;}if(temp>=this.props.max){temp=this.props.max;}temp=this.getTemps(temp);this.setState({temp,});this.props.valueChange(this.state.temp);}getTemps(tmps){constk=parseInt((tmps-this.props.min)/this.props.step,10);constk1=this.props.min+this.props.step*k;constk2=this.props.min+this.props.step*(k+1);if(Math.abs(k1-tmps)>Math.abs(k2-tmps))returnk2;returnk1;}render(){return(<ViewpointerEvents={'box-only'}{...this._panResponder.panHandlers}>{this._renderCircleSvg()}<Viewstyle={{position:'relative',top:-this.props.height/2-this.props.r,left:this.props.width/2-this.props.r,flex:1,}}>{this.props.renderCenterView(this.state.temp)}</View></View>);}_circlerate(){letrate=parseInt((this.state.temp-this.props.min)*100/(this.props.max-this.props.min),10);if(rate<0){rate=0;}elseif(rate>100){rate=100;}returnrate;}_renderCircleSvg(){constcx=this.props.width/2;constcy=this.props.height/2;constprad=this.props.angle/2*(Math.PI/180);conststartX=-(Math.sin(prad)*this.props.r)+cx;conststartY=cy+Math.cos(prad)*this.props.r;////最外层的圆弧配置constendX=Math.sin(prad)*this.props.r+cx;constendY=cy+Math.cos(prad)*this.props.r;//计算进度点constprogress=parseInt(this._circlerate()*(360-this.props.angle)/100,10);//根据象限做处理苦苦苦高中数学全忘了,参考辅助线constt=progress+this.props.angle/2;constprogressX=cx-Math.sin(t*(Math.PI/180))*this.props.r;constprogressY=cy+Math.cos(t*(Math.PI/180))*this.props.r;constdescriptions=['M',startX,startY,'A',this.props.r,this.props.r,0,1,1,endX,endY,].join('');constprogressdescription=['M',startX,startY,'A',this.props.r,this.props.r,0,t>=180+this.props.angle/2?1:0,1,progressX,progressY,].join('');return(<Svgheight={this.props.height}width={this.props.width}style={styles.svg}><Pathd={descriptions}fill="none"stroke={this.props.outArcColor}strokeWidth={this.props.strokeWidth}/><Pathd={progressdescription}fill="none"stroke={this.props.progressvalue}strokeWidth={this.props.strokeWidth}/><Circlecx={progressX}cy={progressY}r={this.props.tabR}stroke={this.props.tabStrokeColor}strokeWidth={this.props.tabStrokeWidth}fill={this.props.tabColor}/></Svg>);}}conststyles=StyleSheet.create({svg:{},});

外部调用

<Viewstyle={styles.container}><CircleProgresswidth={width}height={height}r={r}angle={60}min={5}max={35}step={0.5}value={22}complete={temp=>{}}valueChange={temp=>{}}renderCenterView={temp=>(<Viewstyle={{flex:1}}></View>)}enTouch={true}/></View>
 </div> <div class="zixun-tj-product adv-bottom"></div> </div> </div> <div class="prve-next-news">
本文:怎么用react native实现圆弧拖动进度条的详细内容,希望对您有所帮助,信息来源于网络。
上一篇:ESLine怎么配置及使用下一篇:

16 人围观 / 0 条评论 ↓快速评论↓

(必须)

(必须,保密)

阿狸1 阿狸2 阿狸3 阿狸4 阿狸5 阿狸6 阿狸7 阿狸8 阿狸9 阿狸10 阿狸11 阿狸12 阿狸13 阿狸14 阿狸15 阿狸16 阿狸17 阿狸18