ReactNative实现弧形拖动条

2024-02-05 14:20

本文主要是介绍ReactNative实现弧形拖动条,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

我们直接看效果

先看下面的使用代码

     <CircularSlider5step={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 = () => {/** 记录开始滑动开始时的滑块值、弧度和坐标,用户后续值的计算*/const {value} = this.state;this._moveStartValue = value;// 获取开始移动的弧度this._moveStartRadian = this.getRadianByValue(value);// 根据弧度获取开始的极坐标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); // 当前弧度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; // 避免直接从最小值变为最大值//   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);}};/*** 极坐标转笛卡尔坐标* @param {number} radian - 弧度表示的极角*/polarToCartesian(radian) {const {radius} = this.props;const distance = radius + this._getExtraSize() / 2; // 圆心距离坐标轴的距离const x = distance + radius * Math.sin(radian);const y = distance + radius * Math.cos(radian);return {x, y};}/*** 笛卡尔坐标转极坐标* @param {*} x* @param {*} y*/cartesianToPolar(x, y) {const {radius} = this.props;const distance = radius + this._getExtraSize() / 2; // 圆心距离坐标轴的距离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;}/*** 获取当前弧度*/getCurrentRadian() {return this.getRadianByValue(this.state.value);}/*** 根据滑块的值获取弧度* @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(); // 当前弧度const curPoint = this.polarToCartesian(currentRadian);const contentStyle = [styles.content, contentContainerStyle];return (<ViewonLayout={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}`}><PathstrokeWidth={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}`}/><PathstrokeWidth={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}`}/><Circlecx={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,},
});

这篇关于ReactNative实现弧形拖动条的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/681188

相关文章

Redis客户端连接机制的实现方案

《Redis客户端连接机制的实现方案》本文主要介绍了Redis客户端连接机制的实现方案,包括事件驱动模型、非阻塞I/O处理、连接池应用及配置优化,具有一定的参考价值,感兴趣的可以了解一下... 目录1. Redis连接模型概述2. 连接建立过程详解2.1 连php接初始化流程2.2 关键配置参数3. 最大连

Python实现网格交易策略的过程

《Python实现网格交易策略的过程》本文讲解Python网格交易策略,利用ccxt获取加密货币数据及backtrader回测,通过设定网格节点,低买高卖获利,适合震荡行情,下面跟我一起看看我们的第一... 网格交易是一种经典的量化交易策略,其核心思想是在价格上下预设多个“网格”,当价格触发特定网格时执行买

python设置环境变量路径实现过程

《python设置环境变量路径实现过程》本文介绍设置Python路径的多种方法:临时设置(Windows用`set`,Linux/macOS用`export`)、永久设置(系统属性或shell配置文件... 目录设置python路径的方法临时设置环境变量(适用于当前会话)永久设置环境变量(Windows系统

Python对接支付宝支付之使用AliPay实现的详细操作指南

《Python对接支付宝支付之使用AliPay实现的详细操作指南》支付宝没有提供PythonSDK,但是强大的github就有提供python-alipay-sdk,封装里很多复杂操作,使用这个我们就... 目录一、引言二、准备工作2.1 支付宝开放平台入驻与应用创建2.2 密钥生成与配置2.3 安装ali

Spring Security 单点登录与自动登录机制的实现原理

《SpringSecurity单点登录与自动登录机制的实现原理》本文探讨SpringSecurity实现单点登录(SSO)与自动登录机制,涵盖JWT跨系统认证、RememberMe持久化Token... 目录一、核心概念解析1.1 单点登录(SSO)1.2 自动登录(Remember Me)二、代码分析三、

PyCharm中配置PyQt的实现步骤

《PyCharm中配置PyQt的实现步骤》PyCharm是JetBrains推出的一款强大的PythonIDE,结合PyQt可以进行pythion高效开发桌面GUI应用程序,本文就来介绍一下PyCha... 目录1. 安装China编程PyQt1.PyQt 核心组件2. 基础 PyQt 应用程序结构3. 使用 Q

Python实现批量提取BLF文件时间戳

《Python实现批量提取BLF文件时间戳》BLF(BinaryLoggingFormat)作为Vector公司推出的CAN总线数据记录格式,被广泛用于存储车辆通信数据,本文将使用Python轻松提取... 目录一、为什么需要批量处理 BLF 文件二、核心代码解析:从文件遍历到数据导出1. 环境准备与依赖库

linux下shell脚本启动jar包实现过程

《linux下shell脚本启动jar包实现过程》确保APP_NAME和LOG_FILE位于目录内,首次启动前需手动创建log文件夹,否则报错,此为个人经验,供参考,欢迎支持脚本之家... 目录linux下shell脚本启动jar包样例1样例2总结linux下shell脚本启动jar包样例1#!/bin

go动态限制并发数量的实现示例

《go动态限制并发数量的实现示例》本文主要介绍了Go并发控制方法,通过带缓冲通道和第三方库实现并发数量限制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录带有缓冲大小的通道使用第三方库其他控制并发的方法因为go从语言层面支持并发,所以面试百分百会问到

Go语言并发之通知退出机制的实现

《Go语言并发之通知退出机制的实现》本文主要介绍了Go语言并发之通知退出机制的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1、通知退出机制1.1 进程/main函数退出1.2 通过channel退出1.3 通过cont