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,},
});

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/452813.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue中nextTick方法的作用与原理

在Vue的开发中&#xff0c;你可能会遇到一些异步更新的问题&#xff0c;如在改变数据后需要等待DOM更新完毕后再进行下一步操作。这时就可以使用Vue提供的nextTick方法来解决这个问题。 nextTick方法的作用是在DOM更新之后执行回调函数&#xff0c;确保在下次DOM更新循环结束之…

nginx slice模块的使用和源码分析

文章目录 1. 为什么需要ngx_http_slice_module2. 配置指令3. 加载模块4. 源码分析4.1 指令分析4.2 模块初始化4.3 slice模块的上下文4.2 $slice_range字段值获取4.3 http header过滤处理4.4 http body过滤处理5 测试和验证 1. 为什么需要ngx_http_slice_module 顾名思义&#…

【开源】基于JAVA+Vue+SpringBoot的数据可视化的智慧河南大屏

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统展示四、核心代码4.1 数据模块 A4.2 数据模块 B4.3 数据模块 C4.4 数据模块 D4.5 数据模块 E 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的数据可视化的智慧河南大屏&#xff0c;包含了GDP、…

Cadence——输出文件部分

本文章基于凡亿教育基础入门66讲 &#xff08;一&#xff09;MARK点&#xff0c;工艺边&#xff0c;阻抗说明相关准备文件 &#xff08;1&#xff09;MARK点 a,点击设置&#xff0c;用户偏好设置 b,指定MARK焊盘路径和封装路径 c,点击放置&#xff0c;手动 d,点击高级设置,将…

【快速上手QT】01-QWidgetQMainWindow QT中的窗口

总所周知&#xff0c;QT是一个跨平台的C图形用户界面应用程序开发框架。它既可以开发GUI程序&#xff0c;也可用于开发非GUI程序&#xff0c;当然我们用到QT就是要做GUI的&#xff0c;所以我们快速上手QT的第一篇博文就讲QT的界面窗口。 我用的IDE是VS2019&#xff0c;使用QTc…

ReactNative实现文本渐变

我们直接上图&#xff0c;可以看到上面文本的效果&#xff0c;使用SVG实现 1.首先还是要引入react-native-svg库 2.使用该库下面的LinearGradient和Text 好&#xff0c;话不多说&#xff0c;我们看具体代码 <Svg width{422} height{30} viewBox{0 0 422 30}><Defs&…

管理类联考-复试-全流程演练-导航页

文章目录 整体第一步&#xff1a;学校导师两手抓——知己知彼是关键学校校训历史 导师 第二步&#xff1a;面试问题提前背——押题助沟通自我介绍——出现概率&#xff1a;100%为什么选择这个专业&#xff1f;今后如何打算&#xff1f;你认为自己本科专业和现在所考的专业有什么…

JAVASE进阶:Collection高级(2)——源码剖析ArrayList、LinkedList、迭代器

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位大四、研0学生&#xff0c;正在努力准备大四暑假的实习 &#x1f30c;上期文章&#xff1a;JAVASE进阶&#xff1a;Collection高级&#xff08;1&#xff09;——源码分析contains方法、lambda遍历集合 &#x1f4da;订阅…

Android Studio安装教程

一.Android Studio安装教程 官网 Android Studio 下载最新版本的 Android Studio。官网地址&#xff1a;https://devel oper.android.google.cn/studio。

ChatGPT之搭建API代理服务

简介 一行Docker命令部署的 OpenAI/GPT API代理&#xff0c;支持SSE流式返回、腾讯云函数 。 项目地址&#xff1a;https://github.com/easychen/openai-api-proxy 这个项目可以自行搭建 OpenAI API 代理服务器工具&#xff0c;该项目是代理的服务器端&#xff0c;不是客户端。…

vuecli3 执行 npm run build 打包命令报错:TypeError: file.split is not a function

问题 今天有个项目在打包的时候遇到了一个问题&#xff0c;就是执行 npm run build 命令的时候报错了&#xff0c;如下&#xff1a; 解决 我排查了一下&#xff0c;模拟代码如下&#xff1a;在打包的时候用了 MinChunkSizePlugin const webpack require("webpack"…

【Web】CVE-2021-22448 Log4j RCE漏洞学习

目录 复现流程 漏洞原理 复现流程 启动HTTP->启动LDAP->执行Log4j vps起个http服务,放好Exploit.class这个恶意字节码 LDAPRefServer作为恶意LDAP服务器 import java.net.InetAddress; import java.net.MalformedURLException; import java.net.URL; import javax.ne…