這篇“怎么用react native實(shí)現(xiàn)圓弧拖動(dòng)進(jìn)度條”文章的知識(shí)點(diǎn)大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價(jià)值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“怎么用react native實(shí)現(xiàn)圓弧拖動(dòng)進(jìn)度條”文章吧。

先上效果圖

因?yàn)樾枨笮枰獙?shí)現(xiàn)這個(gè)效果圖 非原生實(shí)現(xiàn),
難點(diǎn)1:繪制 使用svg
難點(diǎn)2:點(diǎn)擊事件的處理
難點(diǎn)3:封裝
由于繪制需要是使用svg
此處自行百度 按照svg以及api 教學(xué)
視圖代碼塊
render() {
return (
<View pointerEvents={'box-only'}
//事件處理
{...this._panResponder.panHandlers}>
//實(shí)際圓環(huán)
{this._renderCircleSvg()}
// 計(jì)算中心距離
<View
style={{
position: 'relative',
top: -this.props.height / 2 - this.props.r,
left: this.props.width / 2 - this.props.r,
flex: 1,
}}>
// 暴露給外部渲染圓環(huán)中心的接口
{this.props.renderCenterView(this.state.temp)}
</View>
</View>
);
_renderCircleSvg() {
//中心點(diǎn)
const cx = this.props.width / 2;
const cy = this.props.height / 2;
//計(jì)算是否有偏差角 對(duì)應(yīng)圖就是下面缺了一塊的
const prad = this.props.angle / 2 * (Math.PI / 180);
//三角計(jì)算起點(diǎn)
const startX = -(Math.sin(prad) * this.props.r) + cx;
const startY = cy + Math.cos(prad) * this.props.r;
//終點(diǎn)
const endX = Math.sin(prad) * this.props.r + cx;
const endY = cy + Math.cos(prad) * this.props.r;
// 計(jì)算進(jìn)度點(diǎn)
const progress = parseInt(
this._circlerate() * (360 - this.props.angle) / 100,
10
);
// 根據(jù)象限做處理 苦苦苦 高中數(shù)學(xué)全忘了,參考輔助線
const t = progress + this.props.angle / 2;
const progressX = cx - Math.sin(t * (Math.PI / 180)) * this.props.r;
const progressY = cy + Math.cos(t * (Math.PI / 180)) * this.props.r;
// SVG的描述 這里百度下就知道什么意思
const descriptions = [
'M',
startX,
startY,
'A',
this.props.r,
this.props.r,
0,
1,
1,
endX,
endY,
].join(' ');
const progressdescription = [
'M',
startX,
startY,
'A',
this.props.r,
this.props.r,
0,
//根據(jù)角度是否是0,1 看下效果就知道了
t >= 180 + this.props.angle / 2 ? 1 : 0,
1,
progressX,
progressY,
].join(' ');
return (
<Svg
height={this.props.height}
width={this.props.width}
style={styles.svg}>
<Path
d={descriptions}
fill="none"
stroke={this.props.outArcColor}
strokeWidth={this.props.strokeWidth} />
<Path
d={progressdescription}
fill="none"
stroke={this.props.progressvalue}
strokeWidth={this.props.strokeWidth} />
<Circle
cx={progressX}
cy={progressY}
r={this.props.tabR}
stroke={this.props.tabStrokeColor}
strokeWidth={this.props.tabStrokeWidth}
fill={this.props.tabColor} />
</Svg>
);
}
}事件處理代碼塊
// 參考react native 官網(wǎng)對(duì)手勢(shì)的講解
iniPanResponder() {
this.parseToDeg = this.parseToDeg.bind(this);
this._panResponder = PanResponder.create({
// 要求成為響應(yīng)者:
onStartShouldSetPanResponder: () => true,
onStartShouldSetPanResponderCapture: () => true,
onMoveShouldSetPanResponder: () => true,
onMoveShouldSetPanResponderCapture: () => true,
onPanResponderGrant: evt => {
// 開始手勢(shì)操作。給用戶一些視覺反饋,讓他們知道發(fā)生了什么事情!
if (this.props.enTouch) {
this.lastTemper = this.state.temp;
const x = evt.nativeEvent.locationX;
const y = evt.nativeEvent.locationY;
this.parseToDeg(x, y);
}
},
onPanResponderMove: (evt, gestureState) => {
if (this.props.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 (this.props.enTouch) this.props.complete(this.state.temp);
},
// 另一個(gè)組件已經(jīng)成為了新的響應(yīng)者,所以當(dāng)前手勢(shì)將被取消。
onPanResponderTerminate: () => {},
// 返回一個(gè)布爾值,決定當(dāng)前組件是否應(yīng)該阻止原生組件成為JS響應(yīng)者
// 默認(rèn)返回true。目前暫時(shí)只支持android。
onShouldBlockNativeResponder: () => true,
});
}
//畫象限看看就知道了 就是和中線點(diǎn)計(jì)算角度
parseToDeg(x, y) {
const cx = this.props.width / 2;
const cy = this.props.height / 2;
let deg;
let temp;
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;
} else if (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;
} else if (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;
} else if (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;
}
//因?yàn)樘峁┎介L(zhǎng),所欲需要做接近步長(zhǎng)的數(shù)
temp = this.getTemps(temp);
this.setState({
temp,
});
this.props.valueChange(this.state.temp);
}
getTemps(tmps) {
const k = parseInt((tmps - this.props.min) / this.props.step, 10);
const k1 = this.props.min + this.props.step * k;
const k2 = this.props.min + this.props.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: this.props.value,
};
this.iniPanResponder();
}
iniPanResponder() {
this.parseToDeg = this.parseToDeg.bind(this);
this._panResponder = PanResponder.create({
// 要求成為響應(yīng)者:
onStartShouldSetPanResponder: () => true,
onStartShouldSetPanResponderCapture: () => true,
onMoveShouldSetPanResponder: () => true,
onMoveShouldSetPanResponderCapture: () => true,
onPanResponderGrant: evt => {
// 開始手勢(shì)操作。給用戶一些視覺反饋,讓他們知道發(fā)生了什么事情!
if (this.props.enTouch) {
this.lastTemper = this.state.temp;
const x = evt.nativeEvent.locationX;
const y = evt.nativeEvent.locationY;
this.parseToDeg(x, y);
}
},
onPanResponderMove: (evt, gestureState) => {
if (this.props.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 (this.props.enTouch) this.props.complete(this.state.temp);
},
// 另一個(gè)組件已經(jīng)成為了新的響應(yīng)者,所以當(dāng)前手勢(shì)將被取消。
onPanResponderTerminate: () => {},
// 返回一個(gè)布爾值,決定當(dāng)前組件是否應(yīng)該阻止原生組件成為JS響應(yīng)者
// 默認(rèn)返回true。目前暫時(shí)只支持android。
onShouldBlockNativeResponder: () => true,
});
}
componentWillReceiveProps(nextProps) {
if (nextProps.value != this.state.temp) {
this.state = {
temp: nextProps.value,
};
}
}
parseToDeg(x, y) {
const cx = this.props.width / 2;
const cy = this.props.height / 2;
let deg;
let temp;
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;
} else if (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;
} else if (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;
} else if (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) {
const k = parseInt((tmps - this.props.min) / this.props.step, 10);
const k1 = this.props.min + this.props.step * k;
const k2 = this.props.min + this.props.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: -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() {
let rate = parseInt(
(this.state.temp - this.props.min) *
100 /
(this.props.max - this.props.min),
10
);
if (rate < 0) {
rate = 0;
} else if (rate > 100) {
rate = 100;
}
return rate;
}
_renderCircleSvg() {
const cx = this.props.width / 2;
const cy = this.props.height / 2;
const prad = this.props.angle / 2 * (Math.PI / 180);
const startX = -(Math.sin(prad) * this.props.r) + cx;
const startY = cy + Math.cos(prad) * this.props.r; // // 最外層的圓弧配置
const endX = Math.sin(prad) * this.props.r + cx;
const endY = cy + Math.cos(prad) * this.props.r;
// 計(jì)算進(jìn)度點(diǎn)
const progress = parseInt(
this._circlerate() * (360 - this.props.angle) / 100,
10
);
// 根據(jù)象限做處理 苦苦苦 高中數(shù)學(xué)全忘了,參考輔助線
const t = progress + this.props.angle / 2;
const progressX = cx - Math.sin(t * (Math.PI / 180)) * this.props.r;
const progressY = cy + Math.cos(t * (Math.PI / 180)) * this.props.r;
const descriptions = [
'M',
startX,
startY,
'A',
this.props.r,
this.props.r,
0,
1,
1,
endX,
endY,
].join(' ');
const progressdescription = [
'M',
startX,
startY,
'A',
this.props.r,
this.props.r,
0,
t >= 180 + this.props.angle / 2 ? 1 : 0,
1,
progressX,
progressY,
].join(' ');
return (
<Svg
height={this.props.height}
width={this.props.width}
style={styles.svg}>
<Path
d={descriptions}
fill="none"
stroke={this.props.outArcColor}
strokeWidth={this.props.strokeWidth} />
<Path
d={progressdescription}
fill="none"
stroke={this.props.progressvalue}
strokeWidth={this.props.strokeWidth} />
<Circle
cx={progressX}
cy={progressY}
r={this.props.tabR}
stroke={this.props.tabStrokeColor}
strokeWidth={this.props.tabStrokeWidth}
fill={this.props.tabColor} />
</Svg>
);
}
}
const styles = StyleSheet.create({
svg: {},
});外部調(diào)用
<View style={styles.container}>
<CircleProgress
width={width}
height={height}
r={r}
angle={60}
min={5}
max={35}
step={0.5}
value={22}
complete={temp => {
}}
valueChange={temp => {}}
renderCenterView={temp => (
<View style={{ flex: 1 }}>
</View>
)}
enTouch={true} />
</View>以上就是關(guān)于“怎么用react native實(shí)現(xiàn)圓弧拖動(dòng)進(jìn)度條”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對(duì)大家有幫助,若想了解更多相關(guān)的知識(shí)內(nèi)容,請(qǐng)關(guān)注創(chuàng)新互聯(lián)-成都網(wǎng)站建設(shè)公司行業(yè)資訊頻道。
網(wǎng)站欄目:怎么用reactnative實(shí)現(xiàn)圓弧拖動(dòng)進(jìn)度條-創(chuàng)新互聯(lián)
地址分享:http://chinadenli.net/article16/dsjpgg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供做網(wǎng)站、域名注冊(cè)、外貿(mào)建站、網(wǎng)站排名、動(dòng)態(tài)網(wǎng)站、定制開發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容