ReactNative實現(xiàn)弧形拖動條的代碼案例
更新時間:2024年02月05日 11:17:44 作者:xvzhengyang
本文介紹了ReactNative實現(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, // 開口弧度,為了便于計算值為實際開口弧度的一半
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, // 開口弧度,為了便于計算值為實際開口弧度的一半
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 = () => {
/*
* 記錄開始滑動開始時的滑塊值、弧度和坐標(biāo),用戶后續(xù)值的計算
*/
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)); // 計算點與圓心連線和 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; // 起點弧度
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實現(xiàn)弧形拖動條的文章就介紹到這了,更多相關(guān)ReactNative弧形拖動條內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Redux DevTools不能顯示數(shù)據(jù)問題
這篇文章主要介紹了Redux DevTools不能顯示數(shù)據(jù)問題及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01
每天一個hooks學(xué)習(xí)之useUnmount
這篇文章主要為大家介紹了每天一個hooks學(xué)習(xí)之useUnmount,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05
React中常見的TypeScript定義實戰(zhàn)教程
這篇文章主要介紹了React中常見的TypeScript定義實戰(zhàn),本文介紹了Fiber結(jié)構(gòu),F(xiàn)iber的生成過程,調(diào)和過程,以及 render 和 commit 兩大階段,需要的朋友可以參考下2022-10-10
react?+?vite?+?ts項目中優(yōu)雅使用.svg文件
這篇文章主要為大家介紹了react?+?vite?+?ts項目中優(yōu)雅使用.svg文件,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
React應(yīng)用中避免白屏現(xiàn)象的方法小結(jié)
在開發(fā)React應(yīng)用程序時,我們都曾遇到過這樣的場景:一個未被捕獲的異常突然中斷了組件的渲染流程,導(dǎo)致用戶界面呈現(xiàn)出一片空白,也就是俗稱的“白屏”現(xiàn)象,本文將探討如何在React應(yīng)用中有效捕獲并處理這些錯誤,避免白屏現(xiàn)象的發(fā)生,需要的朋友可以參考下2024-06-06

