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

相关文章

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略 1. 特权模式限制2. 宿主机资源隔离3. 用户和组管理4. 权限提升控制5. SELinux配置 💖The Begin💖点点关注,收藏不迷路💖 Kubernetes的PodSecurityPolicy(PSP)是一个关键的安全特性,它在Pod创建之前实施安全策略,确保P

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现

基于51单片机的自动转向修复系统的设计与实现

文章目录 前言资料获取设计介绍功能介绍设计清单具体实现截图参考文献设计获取 前言 💗博主介绍:✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师,一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们电子相关专业的大学生,希望您们都共创辉煌!✌💗 👇🏻 精彩专栏 推荐订阅👇🏻 单片机