ReactNative实现弧形拖动条的代码案例

 更新时间:2024年02月05日 11:17:44   作者:xvzhengyang  
本文介绍了ReactNative实现弧形拖动条,本组件使用到了react-native-svg和PanResponder,结合示例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
(福利推荐:【腾讯云】服务器最新限时优惠活动,云服务器1核2G仅99元/年、2核4G仅768元/3年,立即抢购>>>:9i0i.cn/qcloud

(福利推荐:你还在原价购买阿里云服务器?现在阿里云0.8折限时抢购活动来啦!4核8G企业云服务器仅2998元/3年,立即抢购>>>:9i0i.cn/aliyun

我们直接看效果

先看下面的使用代码

     <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 = () => {
    /*
     * 记录开始滑动开始时的滑块值、弧度和坐标,用户后续值的计算
     */
    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 (
      <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,
  },
});

到此这篇关于ReactNative实现弧形拖动条的文章就介绍到这了,更多相关ReactNative弧形拖动条内容请搜索程序员之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持程序员之家!

相关文章

  • react-redux多个组件数据共享的方法

    react-redux多个组件数据共享的方法

    这篇文章主要介绍了react-redux多个组件数据共享的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-08-08
  • React Navigation 路由传参的操作代码

    React Navigation 路由传参的操作代码

    这篇文章主要介绍了React Navigation 路由传参,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-08-08
  • react-router-domV6版本的路由和嵌套路由写法详解

    react-router-domV6版本的路由和嵌套路由写法详解

    本文主要介绍了react-router-domV6版本的路由和嵌套路由写法详解,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • 一文掌握React?组件树遍历技巧

    一文掌握React?组件树遍历技巧

    这篇文章主要为大家介绍了React?组件树遍历技巧的掌握,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • ReactiveCocoa代码实践之-UI组件的RAC信号操作

    ReactiveCocoa代码实践之-UI组件的RAC信号操作

    这篇文章主要介绍了ReactiveCocoa代码实践之-UI组件的RAC信号操作 的相关资料,需要的朋友可以参考下
    2016-04-04
  • 解决react组件渲染两次的问题

    解决react组件渲染两次的问题

    这篇文章主要介绍了解决react组件渲染两次的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-08-08
  • react自适应布局px转rem实现示例详解

    react自适应布局px转rem实现示例详解

    这篇文章主要为大家介绍了react自适应布局px转rem实现示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08
  • 前端面试题必会之前端react面试题

    前端面试题必会之前端react面试题

    在前端面试过程中经常会问到一些面试题,今天小编抽空给大家讲解前端面试题之必会react面试题,需要的朋友可以参考下
    2023-03-03
  • React组件化的一些额外知识点补充

    React组件化的一些额外知识点补充

    React是一个用于构建用户界面的JavaScript库,下面这篇文章主要给大家介绍了关于React组件化的一些额外知识点,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-10-10
  • 详解react应用中的DOM DIFF算法

    详解react应用中的DOM DIFF算法

    这篇文章主要介绍了react应用中的DOM DIFF算法,帮助大家更好的理解和学习使用react,感兴趣的朋友可以了解下
    2021-04-04

最新评论


http://www.vxiaotou.com