ReactNative實(shí)現(xiàn)弧形拖動條的代碼案例
更新時(shí)間:2024年02月05日 11:17:44 作者:xvzhengyang
本文介紹了ReactNative實(shí)現(xiàn)弧形拖動條,本組件使用到了react-native-svg和PanResponder,結(jié)合示例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
我們直接看效果

先看下面的使用代碼
<CircularSlider5
step={2}
min={0}
max={100}
radius={100}
value={30}
onComplete={(changeValue: number) => this.handleEmailSbp(changeValue)}
onChange={(changeValue: number) => this.handleEmailDpd(changeValue)}
contentContainerStyle={styles.contentContainerStyle}
strokeWidth={10}
buttonBorderColor="#3FE3EB"
buttonFillColor="#fff"
buttonStrokeWidth={10}
openingRadian={Math.PI / 4}
buttonRadius={10}
triangleLinerGradient={[
{stop: '0%', color: '#FF7B4C'},
{stop: '50%', color: '#FFFFFF'},
{stop: '100%', color: '#317AF7'},
]}
linearGradient={[
{stop: '0%', color: '#3FE3EB'},
{stop: '100%', color: '#7E84ED'},
]}></CircularSlider5>
{
radius: 100, // 半徑
strokeWidth: 20, // 線寬
openingRadian: Math.PI / 4, // 開口弧度,為了便于計(jì)算值為實(shí)際開口弧度的一半
backgroundTrackColor: '#e8e8e8', // 底部軌道顏色
linearGradient: [
{stop: '0%', color: '#1890ff'},
{stop: '100%', color: '#f5222d'},
], // 漸變色
min: 0, // 最小值
max: 100, // 最大值
buttonRadius: 12, // 按鈕半徑
buttonBorderColor: '#fff', // 按鈕邊框顏色
buttonStrokeWidth: 1, // 按鈕線寬
};本組件使用到了
1.react-native-svg
2.PanResponder
具體代碼如下
import React, {PureComponent} from 'react';
import Svg, {
Path,
G,
Defs,
LinearGradient,
Stop,
Circle,
} from 'react-native-svg';
import {StyleSheet, View, PanResponder} from 'react-native';
export default class CircularSlider extends PureComponent {
static defaultProps = {
radius: 100, // 半徑
strokeWidth: 20, // 線寬
openingRadian: Math.PI / 4, // 開口弧度,為了便于計(jì)算值為實(shí)際開口弧度的一半
backgroundTrackColor: '#e8e8e8', // 底部軌道顏色
linearGradient: [
{stop: '0%', color: '#1890ff'},
{stop: '100%', color: '#f5222d'},
], // 漸變色
min: 0, // 最小值
max: 100, // 最大值
buttonRadius: 12, // 按鈕半徑
buttonBorderColor: '#fff', // 按鈕邊框顏色
buttonStrokeWidth: 1, // 按鈕線寬
};
constructor(props) {
super(props);
this._panResponder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onMoveShouldSetPanResponder: () => false,
onPanResponderGrant: this._handlePanResponderGrant,
onPanResponderMove: this._handlePanResponderMove,
onPanResponderRelease: this._handlePanResponderEnd,
onPanResponderTerminationRequest: () => false,
onPanResponderTerminate: this._handlePanResponderEnd,
});
this.state = {
value: props.value || props.min,
};
this._containerRef = React.createRef();
}
_handlePanResponderGrant = () => {
/*
* 記錄開始滑動開始時(shí)的滑塊值、弧度和坐標(biāo),用戶后續(xù)值的計(jì)算
*/
const {value} = this.state;
this._moveStartValue = value;
// 獲取開始移動的弧度
this._moveStartRadian = this.getRadianByValue(value);
// 根據(jù)弧度獲取開始的極坐標(biāo)
this._startCartesian = this.polarToCartesian(this._moveStartRadian);
// console.log(`開始滑動弧度${this._startCartesian}`);
// console.log(`開始滑動${this._startCartesian.x}:${this._startCartesian.y}`);
};
_handlePanResponderMove = (e, gestureState) => {
const {min, max, step, openingRadian} = this.props;
let {x, y} = this._startCartesian;
x += gestureState.dx;
y += gestureState.dy;
// console.log(`滑動過程中${x}:${y}`);
const radian = this.cartesianToPolar(x, y); // 當(dāng)前弧度
console.log(`滑動過程中的弧度${radian}`);
const ratio =
(this._moveStartRadian - radian) / ((Math.PI - openingRadian) * 2); // 弧度變化所占比例
const diff = max - min; // 最大值和最小值的差
let value;
if (step) {
value = this._moveStartValue + Math.round((ratio * diff) / step) * step;
} else {
value = this._moveStartValue + ratio * diff;
}
// 處理極值
value = Math.max(min, Math.min(max, value));
this.setState({
value,
});
// this.setState(({value: curValue}) => {
// value = Math.abs(value - curValue) > diff / 4 ? curValue : value; // 避免直接從最小值變?yōu)樽畲笾?
// return {value: Math.round(value)};
// });
this._fireChangeEvent('onChange');
};
_handlePanResponderEnd = (e, gestureState) => {
if (this.props.disabled) {
return;
}
this._fireChangeEvent('onComplete');
};
_fireChangeEvent = event => {
if (this.props[event]) {
this.props[event](this.state.value);
}
};
/**
* 極坐標(biāo)轉(zhuǎn)笛卡爾坐標(biāo)
* @param {number} radian - 弧度表示的極角
*/
polarToCartesian(radian) {
const {radius} = this.props;
const distance = radius + this._getExtraSize() / 2; // 圓心距離坐標(biāo)軸的距離
const x = distance + radius * Math.sin(radian);
const y = distance + radius * Math.cos(radian);
return {x, y};
}
/**
* 笛卡爾坐標(biāo)轉(zhuǎn)極坐標(biāo)
* @param {*} x
* @param {*} y
*/
cartesianToPolar(x, y) {
const {radius} = this.props;
const distance = radius + this._getExtraSize() / 2; // 圓心距離坐標(biāo)軸的距離
if (x === distance) {
return y > distance ? 0 : Math.PI / 2;
}
const a = Math.atan((y - distance) / (x - distance)); // 計(jì)算點(diǎn)與圓心連線和 x 軸的夾角
return (x < distance ? (Math.PI * 3) / 2 : Math.PI / 2) - a;
}
/**
* 獲取當(dāng)前弧度
*/
getCurrentRadian() {
return this.getRadianByValue(this.state.value);
}
/**
* 根據(jù)滑塊的值獲取弧度
* @param {*} value
*/
getRadianByValue(value) {
const {openingRadian, min, max} = this.props;
return (
((Math.PI - openingRadian) * 2 * (max - value)) / (max - min) +
openingRadian
);
}
/**
* 獲取除半徑外額外的大小,返回線寬和按鈕直徑中較大的
*/
_getExtraSize() {
const {strokeWidth, buttonRadius, buttonStrokeWidth} = this.props;
return Math.max(strokeWidth, (buttonRadius + buttonStrokeWidth) * 2);
}
_onLayout = () => {
const ref = this._containerRef.current;
if (ref) {
ref.measure((x, y, width, height, pageX, pageY) => {
this.vertexX = pageX;
this.vertexY = pageY;
});
}
};
render() {
const {
radius,
strokeWidth,
backgroundTrackColor,
openingRadian,
linearGradient,
buttonRadius,
buttonBorderColor,
buttonFillColor,
buttonStrokeWidth,
style,
contentContainerStyle,
children,
} = this.props;
const svgSize = radius * 2 + this._getExtraSize();
const startRadian = 2 * Math.PI - openingRadian; // 起點(diǎn)弧度
const startPoint = this.polarToCartesian(startRadian);
const endPoint = this.polarToCartesian(openingRadian);
const currentRadian = this.getCurrentRadian(); // 當(dāng)前弧度
const curPoint = this.polarToCartesian(currentRadian);
const contentStyle = [styles.content, contentContainerStyle];
return (
<View
onLayout={this._onLayout}
ref={this._containerRef}
style={[styles.container, style]}>
<Svg width={svgSize} height={svgSize}>
<Defs>
<LinearGradient x1="0%" y1="100%" x2="100%" y2="0%" id="gradient">
{linearGradient.map((item, index) => (
<Stop key={index} offset={item.stop} stopColor={item.color} />
))}
</LinearGradient>
</Defs>
<G rotation={0} origin={`${svgSize / 2}, ${svgSize / 2}`}>
<Path
strokeWidth={strokeWidth}
stroke={backgroundTrackColor}
fill="none"
strokeLinecap="round"
d={`M${startPoint.x},${startPoint.y} A ${radius},${radius},0,${
startRadian - openingRadian >= Math.PI ? '1' : '0'
},1,${endPoint.x},${endPoint.y}`}
/>
<Path
strokeWidth={strokeWidth}
stroke="url(#gradient)"
fill="none"
strokeLinecap="round"
d={`M${startPoint.x},${startPoint.y} A ${radius},${radius},0,${
startRadian - currentRadian >= Math.PI ? '1' : '0'
},1,${curPoint.x},${curPoint.y}`}
/>
<Circle
cx={curPoint.x}
cy={curPoint.y}
r={buttonRadius}
fill={buttonFillColor || buttonBorderColor}
stroke={buttonBorderColor}
strokeWidth={buttonStrokeWidth}
{...this._panResponder.panHandlers}
/>
</G>
</Svg>
<View style={contentStyle} pointerEvents="box-none">
{children}
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
justifyContent: 'center',
alignItems: 'center',
},
content: {
position: 'absolute',
left: 0,
top: 0,
bottom: 0,
right: 0,
},
});到此這篇關(guān)于ReactNative實(shí)現(xiàn)弧形拖動條的文章就介紹到這了,更多相關(guān)ReactNative弧形拖動條內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
react-intl實(shí)現(xiàn)React國際化多語言的方法
這篇文章主要介紹了react-intl實(shí)現(xiàn)React國際化多語言的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
react如何使用mobx6動態(tài)加載數(shù)據(jù)
MobX是一個(gè)強(qiáng)大而簡單的狀態(tài)管理工具,它可以幫助我們更好地組織和管理React應(yīng)用程序中的數(shù)據(jù)流,本文給大家介紹react如何使用mobx6動態(tài)加載數(shù)據(jù),感興趣的朋友跟隨小編一起看看吧2024-02-02
React、Vue中key的作用詳解 (key的內(nèi)部原理解析)
key是虛擬DOM對象的標(biāo)識,當(dāng)狀態(tài)中的數(shù)據(jù)發(fā)生變化時(shí),Vue會根據(jù)[新數(shù)據(jù)]生成[新的虛擬DOM],本文給大家介紹React、Vue中key的作用詳解 (key的內(nèi)部原理解析),感興趣的朋友一起看看吧2023-10-10
react事件對象無法獲取offsetLeft,offsetTop,X,Y等元素問題及解決
這篇文章主要介紹了react事件對象無法獲取offsetLeft,offsetTop,X,Y等元素問題及解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。2022-08-08

