版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
【移动应用开发技术】怎么用reactnative实现圆弧拖动进度条
这篇“怎么用reactnative实现圆弧拖动进度条”文章的知识点大部分人都不太理解,所以在下给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“怎么用reactnative实现圆弧拖动进度条”文章吧。先上效果图因为需求需要实现这个效果图非原生实现,难点1:绘制使用svg难点2:点击事件的处理难点3:封装由于绘制需要是使用svg此处自行百度按照svg以及api教学视图代码块
render()
{
return
(
<View
pointerEvents={'box-only'}
//事件处理
{...this._panResponder.panHandlers}>
//实际圆环
{this._renderCircleSvg()}
//
计算中心距离
<View
style={{
position:
'relative',
top:
-ps.height
/
2
-
ps.r,
left:
ps.width
/
2
-
ps.r,
flex:
1,
}}>
//
暴露给外部渲染圆环中心的接口
{ps.renderCenterView(this.state.temp)}
</View>
</View>
);
_renderCircleSvg()
{
//中心点
const
cx
=
ps.width
/
2;
const
cy
=
ps.height
/
2;
//计算是否有偏差角
对应图就是下面缺了一块的
const
prad
=
ps.angle
/
2
*
(Math.PI
/
180);
//三角计算起点
const
startX
=
-(Math.sin(prad)
*
ps.r)
+
cx;
const
startY
=
cy
+
Math.cos(prad)
*
ps.r;
//终点
const
endX
=
Math.sin(prad)
*
ps.r
+
cx;
const
endY
=
cy
+
Math.cos(prad)
*
ps.r;
//
计算进度点
const
progress
=
parseInt(
this._circlerate()
*
(360
-
ps.angle)
/
100,
10
);
//
根据象限做处理
苦苦苦
高中数学全忘了,参考辅助线
const
t
=
progress
+
ps.angle
/
2;
const
progressX
=
cx
-
Math.sin(t
*
(Math.PI
/
180))
*
ps.r;
const
progressY
=
cy
+
Math.cos(t
*
(Math.PI
/
180))
*
ps.r;
//
SVG的描述
这里百度下就知道什么意思
const
descriptions
=
[
'M',
startX,
startY,
'A',
ps.r,
ps.r,
0,
1,
1,
endX,
endY,
].join('
');
const
progressdescription
=
[
'M',
startX,
startY,
'A',
ps.r,
ps.r,
0,
//根据角度是否是0,1
看下效果就知道了
t
>=
180
+
ps.angle
/
2
?
1
:
0,
1,
progressX,
progressY,
].join('
');
return
(
<Svg
height={ps.height}
width={ps.width}
style={styles.svg}>
<Path
d={descriptions}
fill="none"
stroke={ps.outArcColor}
strokeWidth={ps.strokeWidth}
/>
<Path
d={progressdescription}
fill="none"
stroke={gressvalue}
strokeWidth={ps.strokeWidth}
/>
<Circle
cx={progressX}
cy={progressY}
r={ps.tabR}
stroke={ps.tabStrokeColor}
strokeWidth={ps.tabStrokeWidth}
fill={ps.tabColor}
/>
</Svg>
);
}
}事件处理代码块//
参考react
native
官网对手势的讲解
iniPanResponder()
{
this.parseToDeg
=
this.parseToDeg.bind(this);
this._panResponder
=
PanResponder.create({
//
要求成为响应者:
onStartShouldSetPanResponder:
()
=>
true,
onStartShouldSetPanResponderCapture:
()
=>
true,
onMoveShouldSetPanResponder:
()
=>
true,
onMoveShouldSetPanResponderCapture:
()
=>
true,
onPanResponderGrant:
evt
=>
{
//
开始手势操作。给用户一些视觉反馈,让他们知道发生了什么事情!
if
(ps.enTouch)
{
this.lastTemper
=
this.state.temp;
const
x
=
evt.nativeEvent.locationX;
const
y
=
evt.nativeEvent.locationY;
this.parseToDeg(x,
y);
}
},
onPanResponderMove:
(evt,
gestureState)
=>
{
if
(ps.enTouch)
{
let
x
=
evt.nativeEvent.locationX;
let
y
=
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
(ps.enTouch)
plete(this.state.temp);
},
//
另一个组件已经成为了新的响应者,所以当前手势将被取消。
onPanResponderTerminate:
()
=>
{},
//
返回一个布尔值,决定当前组件是否应该阻止原生组件成为JS响应者
//
默认返回true。目前暂时只支持android。
onShouldBlockNativeResponder:
()
=>
true,
});
}
//画象限看看就知道了
就是和中线点计算角度
parseToDeg(x,
y)
{
const
cx
=
ps.width
/
2;
const
cy
=
ps.height
/
2;
let
deg;
let
temp;
if
(x
>=
cx
&&
y
<=
cy)
{
deg
=
Math.atan((cy
-
y)
/
(x
-
cx))
*
180
/
Math.PI;
temp
=
(270
-
deg
-
ps.angle
/
2)
/
(360
-
ps.angle)
*
(ps.max
-
ps.min)
+
ps.min;
}
else
if
(x
>=
cx
&&
y
>=
cy)
{
deg
=
Math.atan((cy
-
y)
/
(cx
-
x))
*
180
/
Math.PI;
temp
=
(270
+
deg
-
ps.angle
/
2)
/
(360
-
ps.angle)
*
(ps.max
-
ps.min)
+
ps.min;
}
else
if
(x
<=
cx
&&
y
<=
cy)
{
deg
=
Math.atan((x
-
cx)
/
(y
-
cy))
*
180
/
Math.PI;
temp
=
(180
-
ps.angle
/
2
-
deg)
/
(360
-
ps.angle)
*
(ps.max
-
ps.min)
+
ps.min;
}
else
if
(x
<=
cx
&&
y
>=
cy)
{
deg
=
Math.atan((cx
-
x)
/
(y
-
cy))
*
180
/
Math.PI;
if
(deg
<
ps.angle
/
2)
{
deg
=
ps.angle
/
2;
}
temp
=
(deg
-
ps.angle
/
2)
/
(360
-
ps.angle)
*
(ps.max
-
ps.min)
+
ps.min;
}
if
(temp
<=
ps.min)
{
temp
=
ps.min;
}
if
(temp
>=
ps.max)
{
temp
=
ps.max;
}
//因为提供步长,所欲需要做接近步长的数
temp
=
this.getTemps(temp);
this.setState({
temp,
});
ps.valueChange(this.state.temp);
}
getTemps(tmps)
{
const
k
=
parseInt((tmps
-
ps.min)
/
ps.step,
10);
const
k1
=
ps.min
+
ps.step
*
k;
const
k2
=
ps.min
+
ps.step
*
(k
+
1);
if
(Math.abs(k1
-
tmps)
>
Math.abs(k2
-
tmps))
return
k2;
return
k1;
}完整代码块import
React,
{
Component
}
from
'react';
import
{
View,
StyleSheet,
PanResponder,
Platform,
Text
}
from
'react-native';
import
Svg,
{
Circle,
Path
}
from
'react-native-svg';
export
default
class
CircleView
extends
Component
{
static
propTypes
=
{
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,
};
static
defaultProps
=
{
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:
ps.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
(ps.enTouch)
{
this.lastTemper
=
this.state.temp;
const
x
=
evt.nativeEvent.locationX;
const
y
=
evt.nativeEvent.locationY;
this.parseToDeg(x,
y);
}
},
onPanResponderMove:
(evt,
gestureState)
=>
{
if
(ps.enTouch)
{
let
x
=
evt.nativeEvent.locationX;
let
y
=
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
(ps.enTouch)
plete(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)
{
const
cx
=
ps.width
/
2;
const
cy
=
ps.height
/
2;
let
deg;
let
temp;
if
(x
>=
cx
&&
y
<=
cy)
{
deg
=
Math.atan((cy
-
y)
/
(x
-
cx))
*
180
/
Math.PI;
temp
=
(270
-
deg
-
ps.angle
/
2)
/
(360
-
ps.angle)
*
(ps.max
-
ps.min)
+
ps.min;
}
else
if
(x
>=
cx
&&
y
>=
cy)
{
deg
=
Math.atan((cy
-
y)
/
(cx
-
x))
*
180
/
Math.PI;
temp
=
(270
+
deg
-
ps.angle
/
2)
/
(360
-
ps.angle)
*
(ps.max
-
ps.min)
+
ps.min;
}
else
if
(x
<=
cx
&&
y
<=
cy)
{
deg
=
Math.atan((x
-
cx)
/
(y
-
cy))
*
180
/
Math.PI;
temp
=
(180
-
ps.angle
/
2
-
deg)
/
(360
-
ps.angle)
*
(ps.max
-
ps.min)
+
ps.min;
}
else
if
(x
<=
cx
&&
y
>=
cy)
{
deg
=
Math.atan((cx
-
x)
/
(y
-
cy))
*
180
/
Math.PI;
if
(deg
<
ps.angle
/
2)
{
deg
=
ps.angle
/
2;
}
temp
=
(deg
-
ps.angle
/
2)
/
(360
-
ps.angle)
*
(ps.max
-
ps.min)
+
ps.min;
}
if
(temp
<=
ps.min)
{
temp
=
ps.min;
}
if
(temp
>=
ps.max)
{
temp
=
ps.max;
}
temp
=
this.getTemps(temp);
this.setState({
temp,
});
ps.valueChange(this.state.temp);
}
getTemps(tmps)
{
const
k
=
parseInt((tmps
-
ps.min)
/
ps.step,
10);
const
k1
=
ps.min
+
ps.step
*
k;
const
k2
=
ps.min
+
ps.step
*
(k
+
1);
if
(Math.abs(k1
-
tmps)
>
Math.abs(k2
-
tmps))
return
k2;
return
k1;
}
render()
{
return
(
<View
pointerEvents={'box-only'}
{...this._panResponder.panHandlers}>
{this._renderCircleSvg()}
<View
style={{
position:
'relative',
top:
-ps.height
/
2
-
ps.r,
left:
ps.width
/
2
-
ps.r,
flex:
1,
}}>
{ps.renderCenterView(this.state.temp)}
</View>
</View>
);
}
_circlerate()
{
let
rate
=
parseInt(
(this.state.temp
-
ps.min)
*
100
/
(ps.max
-
ps.min),
10
);
if
(rate
<
0)
{
rate
=
0;
}
else
if
(rate
>
100)
{
rate
=
100;
}
return
rate;
}
_renderCircleSvg()
{
const
cx
=
ps.width
/
2;
const
cy
=
ps.height
/
2;
const
prad
=
ps.angle
/
2
*
(Math.PI
/
180);
const
startX
=
-(Math.sin(prad)
*
ps.r)
+
cx;
const
startY
=
cy
+
Math.cos(prad)
*
ps.r;
//
//
最外层的圆弧配置
const
endX
=
Math.sin(prad)
*
ps.r
+
cx;
const
endY
=
cy
+
Math.cos(prad)
*
ps.r;
//
计算进度点
const
progress
=
parseInt(
this._circlerate()
*
(360
-
ps.angle)
/
100,
10
);
//
根据象限做处理
苦苦苦
高中数学全忘了,参考辅助线
const
t
=
progress
+
ps.angle
/
2;
const
progressX
=
cx
-
Math.sin(t
*
(Math.PI
/
180))
*
ps.r;
const
progressY
=
cy
+
Math.cos(t
*
(Math.PI
/
180))
*
ps.r;
const
descriptions
=
[
'M',
startX,
s
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 媒介融合背景下的“融合新闻”发展探微
- 学位论文写作对研究生创新能力的影响研究
- LWD随钻测井解释技术研究
- 肺炎疫情群防群控应急预案
- 道路不畅区域应急通行预案
- 数字经济如何影响碳排放强度基于产业结构高级化与合理化的双重视角
- 鸡肉的营养价值与功能
- 公司治理视角下金正大财务舞弊案例分析
- 南通公共交通应急预案
- 孕妇发热急救预案
- 电信线路安装维修安全技术操作规程
- 智能电网大数据知到章节答案智慧树2023年长安大学
- 人教版七年级下册英语unit 6单元全套课件
- 启封密闭、排放瓦斯专项辨识
- 2022-2023学年湖北省武汉市部分重点中学高一(下)期中英语试卷及答案解析
- 红船精神与时代价值知到章节答案智慧树2023年嘉兴学院
- GJB9001C-2017内审检查表汇编
- 警犬工作管理系统
- 电气施工四措一案
- 论幼儿园体育游戏活动对幼儿发展的重要性
- GB20424-2006重金属精矿产品中有害元素的限量规范.pdf
评论
0/150
提交评论