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实现弧形拖动条
我们直接看效果 先看下面的使用代码 <CircularSlider5step{2}min{0}max{100}radius{100}value{30}onComplete{(changeValue: number) > this.handleEmailSbp(changeValue)}onChange{(changeValue: number) > this.handleEmailDpd(changeValue)}contentContainerStyle{…...

STM32F407移植OpenHarmony笔记9
继上一篇笔记,已经完成liteos内核的基本功能适配。 今天尝试启动OHOS和XTS兼容性测试。 如何启动OHOS? OHOS系统初始化接口是OHOS_SystemInit(void),在内核初始化完成后,就能调用。 extern void OHOS_SystemInit(void); OHOS_Sys…...

telnet笔记
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、场景二、介绍1.测试端口2.访问百度3. 简单的爬虫 前言 最近telnet命令用的比较多,所以记录一下。 一、场景 ping应该是大家最常用的命令&…...

【考研408】操作系统笔记
文章目录 [toc] 计算机系统概述操作系统的基本概念操作系统的概念和特征操作系统的目标和功能(**处理器管理、存储器管理、设备管理、文件管理、向用户提供接口、扩充机器**) 操作系统的发展与分类操作系统的运行环境操作系统的运行机制 操作系统的体系结…...

从0开始搭建、上传npm包
从0开始搭建、上传npm包 1、上传一个简单获取水果价格的包创建 vite 项目在项目根目录 src 文件夹中创建 index.ts 文件,文件内容如下:在 main.ts 文件中导入、导出上面创建的方法创建 vite.config.ts 配置文件,文件内容如下配置 package.jso…...
【Go】在 JSON 中解析 time.Duration
当解析 JSON 时,使用time.Duration可能是一个繁琐的过程,因为它需要在一秒的后面添加 9 个零(即 1000000000)。为了简化这个过程,我创建了一个名为 Duration 的新类型: type Duration time.Duration为了将…...

UE4 C++ UGameInstance实例化
1.创建GameInstance C类 2.在.h添加变量 class 工程名称_API UMyGameInstance : public UGameInstance {GENERATED_BODY()public: //定义了三个公开的变量UMyGameInstance();UPROPERTY(EditAnywhere, BlueprintReadWrite, Category "MyGameInstance")FString Name…...

在工业制造方面,如何更好地实现数字化转型?
实现工业制造的数字化转型涉及利用数字技术来增强流程、提高效率并推动创新。以下是工业制造领域更好实现数字化转型的几个关键步骤: 1.定义明确的目标: 清楚地概述您的数字化转型目标。确定需要改进的领域,例如运营效率、产品质量或供应链…...
【MySQL】-10 MySQL 存储过程
MySQL 存储过程 优点缺点一、存储过程的创建和调用创建存储过程实例1、in 输入参数2、out输出参数3、inout输入参数 三、变量1. 变量定义2. 变量赋值3. 用户变量 四、注释MySQL存储过程的调用MySQL存储过程的查询MySQL存储过程的修改MySQL存储过程的删除MySQL存储过程的控制语句…...
3.闭包 - JS
作用域 一般认为 JS 中作用域有三种: 全局作用域:一个脚本运行代码的默认作用域;模块作用域:一个模块运行代码的默认作用域;函数作用域:一个函数运行代码的默认作用域。 而由于 let/const 声明变量的作用…...

Java实现批量视频抽帧2.0
继上个版本 对其进行略微升级 🤓 上个版本仅对一个视频进行抽帧处理 此版本可对一个文件夹内的全部视频进行抽帧并对应的文件夹进行帧图片的保存 1️⃣配置pom.xml (保持上次不变) <dependencies><dependency><grou…...
MFC 原生LsitCtrl单元格嵌入图标
// ListItemInsertIconDlg.h: 头文件 //#pragma once// CListItemInsertIconDlg 对话框 class CListItemInsertIconDlg : public CDialogEx { // 构造 public:CListItemInsertIconDlg(CWnd* pParent nullptr); // 标准构造函数// 对话框数据 #ifdef AFX_DESIGN_TIMEenum { IDD…...

黑马头条 - minio
我是南城余!阿里云开发者平台专家博士证书获得者! 欢迎关注我的博客!一同成长! 一名从事运维开发的worker,记录分享学习。 专注于AI,运维开发,windows Linux 系统领域的分享! 知…...

认识Tomcat (一)
认识Tomcat (一) 一、服务器 1.1 服务器简介 硬件服务器的构成与一般的PC比较相似,但是服务器在稳定性、安全性、性能等方面都要求更高,因为CPU、芯片组、内存、磁盘系统、网络等硬件和普通PC有所不同。 软件服务器&…...

SSH免密切换服务器案例-ssh协议(公钥和私钥)
公钥和私钥理解 公钥提供加密,私钥解密,公钥可以共享,私钥不可以。举例公钥相当于锁头,可以给别人用,钥匙相当于私钥,只能开自己发出去的锁头,也就是私钥和公钥成对,私钥只能解密对…...

AMH面板如何安装与公网远程访问本地面板界面
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...

【教3妹学编程-算法题】1696. 跳跃游戏 VI
3妹:好冷啊, 冻得瑟瑟发抖啦 2哥 : 没想到都立春了还这么冷啊~ 3妹:暴雪、冻雨、大雨,这天气还让不让人活啦!!! 2哥 :哎,好多人都滞留的高铁站了,没法回家了 3妹…...
解决C#中无限递归导致的System.StackOverflowException异常
目录 背景: 错误示例分析: 为什么是错误的? 正确的使用递归: 修改后的代码: 原理和原因: 结论: 背景: 在软件开发中,递归是一种常见的编程技术,它允许方法调用自…...

ASP.NET Core 预防开放式重定向攻击
写在前面 为预防钓鱼网站的常用套路,在进行 Web 应用程序的开发时,原则上应该将所有由用户提交的数据视为不可信。如果应用程序中包含了基于 URL 内容重定向的功能,需要确保这种类型的重定向操作只能在应用本地完成,或者明确判断…...

HashCat 恢复Excel、Word、PPT密码保姆教程
HashCat 恢复Excel、Word、PPT密码 一、流程 整体需要两个步骤 先用office2john.py获取下文件的hash值 python office2john.py 1.xlsx > hash这个命令需要你电脑有python环境,然后在cmd命令窗口中执行此命令就行 文件链接:https://github.com/magnu…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...

CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...

(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...