React-Native关于色盘的探索

最近因在学习RN的混合开发,项目中有个色盘的需求,难为了好几天才搞出来,现在记录下色盘实现,话不多说,开始敲代码:

在研究色盘的绘制之前 我们先来看下RN中我们使用到的几个手势参数

RN手势
// 返回值为布尔值, 如果返回值为 true,则表示这个 View 能够响应滑动手势, 两者有一个为true即可响应
onMoveShouldSetPanResponder: (e, gestureState) => {...}
onMoveShouldSetPanResponderCapture: (e, gestureState) => {...}

// 返回值为布尔值, 如果返回值为 true,则表示这个 View 能够响应触摸手势, 两者有一个为true即可响应
onStartShouldSetPanResponder: (e, gestureState) => {...}
onStartShouldSetPanResponderCapture: (e, gestureState) => {...}

onPanResponderRelease: (e, gestureState) => {...}
// 用户滑动手指时,调用该方法
onPanResponderMove: (e, gestureState) => {...}

// 返回值为布尔值, 如果返回值为 true,则表示这个 View 能够响应触摸手势
onStartShouldSetResponder={(evt) => {}
// 手势点击时候的事件
onResponderRelease={(evt) => {}
参数event(e)

获取触摸的位置在被响应的 View 中的相对坐标,evt.nativeEvent.locationX
nativeEvent
changedTouches - 在上一次事件之后,所有发生变化的触摸事件的数组集合(即上一次事件后,所有移动过的触摸点)
identifier - 触摸点的ID
locationX - 触摸点相对于父元素的横坐标
locationY - 触摸点相对于父元素的纵坐标
pageX - 触摸点相对于根元素的横坐标
pageY - 触摸点相对于根元素的纵坐标
target - 触摸点所在的元素ID
timestamp - 触摸事件的时间戳,可用于移动速度的计算
touches - 当前屏幕上的所有触摸点的集合

gestureState对象

stateID – 触摸状态的ID。在屏幕上有至少一个触摸点的情况下,这个ID会一直有效。
moveX - 最近一次移动时的屏幕横坐标
moveY - 最近一次移动时的屏幕纵坐标
x0 - 当响应器产生时的屏幕坐标
y0 - 当响应器产生时的屏幕坐标
dx - 从触摸操作开始时的累计横向路程
dy - 从触摸操作开始时的累计纵向路程
vx - 当前的横向移动速度
vy - 当前的纵向移动速度
numberActiveTouches - 当前在屏幕上的有效触摸点的数量

代码实现:

书写UI:
render() {
    const { radius } = this.state


    return (
        <View
        
        ref={node => {this.self = node}}
          onLayout={nativeEvent => this.onLayout(nativeEvent)}
          style={[styles.coverResponder, this.props.style]}>
          
          <View style={[styles.img, {
                        height: radius * 2 ,
                        width: radius * 2,
                        }]}>
           // 色盘
          <Image style={{
                        height: radius * 2 ,
                        width: radius * 2,
                        alignItems:'center',
                        position:'absolute'}}
            source={require('../images/colorwheel_image_caiguang.png')}
            onStartShouldSetResponder={(evt) => {
            
            return true
         }}
         onResponderMove={(evt) => {
            return false
            }}
         
         onResponderRelease={(evt) => {
          this.handleTapData(evt)
          this.updateColor(evt.nativeEvent.locationX,evt.nativeEvent.locationY,false)
            
         }}/>
         // 此View是图标的白色底部view
          <View
            {...this._panResponder.panHandlers}
            
              style={{
                      marginTop:this.state.offset.y,
                      marginLeft:this.state.offset.x,
                      width: this.state.thumbSizeW,
                      height: this.state.thumbSizeW,
                      position:'absolute',
                      backgroundColor: '#fff',
                      borderRadius: this.state.thumbSizeW / 2,
                    }}>
              //此View是填充颜色的View
              <View
              style={{
                marginTop:3,
                marginLeft:3,
                width: this.state.thumbSizeW - 6,
                height: this.state.thumbSizeW - 6,
                position:'absolute',
                backgroundColor: this.state.currentColor,
                borderRadius: (this.state.thumbSizeW - 6) / 2,
              }}>
              </View>
          </View>
        </View>
          
      </View>
    )
  }

1、onResponderRelease={(evt): 这个点击事件是点击色盘的时候,定位图标的位置
2、{...this._panResponder.panHandlers}: 监视器与监视区域关联,把图标和手势关联起来

定义手势
UNSAFE_componentWillMount() {
    // 图标的手势
    this._panResponder = PanResponder.create({
      
        onMoveShouldSetPanResponder:  (evt, gestureState) => {
            return true;
        },
        onPanResponderMove: (evt, gestureState) => {
            this.handlerData(evt,gestureState,true)
            // this.updateColor(evt)
        },
        onPanResponderRelease: (evt, gestureState) => {

          this.handlerData(evt,gestureState,false)
          // 记录滑动前 图表的位置
          this.lastX = this.state.offset.x;
          this.lastY = this.state.offset.y;
        },
      });

  }
关键的数据处理
handleTapData(evt,) {
    console.log('开始点击',[evt.nativeEvent.locationX,evt.nativeEvent.locationY])

    let tapX = evt.nativeEvent.locationX
    let tapY = evt.nativeEvent.locationY
    let result = this.getPointValues(tapX,tapY)
    let maxX = result.maxX
    let maxY = result.maxY
    this.setState({
      offset:{
        x: maxX,
        y: maxY,
    }
    })
    this.lastX = maxX ;
    this.lastY = maxY ;
  }

  handlerData(evt,ges,moving) {
      console.log('开始滑动',[this.lastX,this.lastY])
      
      //此时视图的坐标
      let locationX = this.lastX +  ges.dx + this.state.thumbSizeW / 2
      let locationY = this.lastY + ges.dy + this.state.thumbSizeW / 2
      // 获取在父元素的实际坐标点值
      let result = this.getPointValues(locationX,locationY)
      let maxX = result.maxX
      let maxY = result.maxY
      // 获取此点的颜色值
      this.updateColor(locationX,locationY,moving)

      this.setState({
        offset:{
            x: maxX ,
            y: maxY ,
        }
    })
   

  }

  // 获取实际坐标的 角度 坐标值 步长
  getPointValues(locationX,locationY) {
    //半径
    let radius = this.state.radius
    // 求斜边的长度
    let offsetX = Math.abs(radius - locationX)
    let offsetY = Math.abs(radius  - locationY)
    let length = Math.sqrt(offsetX * offsetX + offsetY * offsetY)// 斜边长度

    //求角度
    // let angle = Math.atan2(offsetY, offsetX) * (180 / Math.PI)
    let angle = this.getPointAngle(radius - locationX,radius  - locationY)


    // console.log('是否出界'+[locationX,locationY,angle])
    // 最终的坐标
    let maxX = locationX - this.state.thumbSizeW / 2//this.lastX + ges.dx
    let maxY = locationY - this.state.thumbSizeW / 2//this.lastY + ges.dy

    if (length > this.state.radius) {
      // 超出边界
      let maxOffsetX = this.state.radius * (locationX - radius) / length
      let maxOffsetY = this.state.radius * (radius - locationY) / length
      maxX = radius + maxOffsetX - this.state.thumbSizeW / 2
      maxY =  radius - maxOffsetY - this.state.thumbSizeW / 2
      length = this.state.radius
    }
    return {
      angle: angle,
      maxX: maxX,
      maxY: maxY,
      length: length,
    }

  }

  getPointAngle(x,y) {
    let radius = this.state.radius
    // 此时的角度是从左边为 0°开始 顺时针 到右边为 180° 逆时针到最右边为 -180° 
    let angle = Math.atan2(y, x) * (180 / Math.PI)
    // 此转换为左边 0° 顺时针转一圈为 360°
    angle = 180 - angle 
    console.log('获取角度'+angle)
    return angle
  }

 // 根据颜色值更新图标的位置 color : #ffffff
  updateUIWithColor(color) {
    const {radius} = this.state
    // 根据初始的颜色值回去图标的坐标
    const hsv = colorsys.hex2Hsv(color)
    console.log('初始值颜色'+[hsv.h,hsv.s,hsv.v])
    
    //角度
    let angle = 180 - hsv.h
    // 在色盘中的步长
    let  length = (radius / 100) * hsv.s
    // 
    let offsett = this.hypotenuse(length,angle)
    offx = radius - offsett.x - this.state.thumbSizeW / 2
    offy = radius - offsett.y - this.state.thumbSizeW / 2
   
    this.lastX = offx;
    this.lastY = offy;
    this.setState({
      currentColor: color,
      offset:{
        x: offx,
        y: offy,
      }
    })
  }

  //已知角度和斜边,求直角边
  hypotenuse(long,angle) {
    //获得弧度
    var radian = 2*Math.PI/360*angle;
    return {
        x:Math.cos(radian) * long,
        y:Math.sin(radian) * long
    };
  } 
 

 // 根据坐标的位置 获取颜色值
  updateColor(x,y, moving) {
    const {angle,maxX, maxY,length} = this.getPointValues(x,y)
    let radius = length / this.state.radius
    const rad = radius > 1 ? 1 : radius
    const hsv = {h:angle, s: 100 * rad, v: 100};

    const currentColor = colorsys.hsv2Hex(hsv)
    this.setState({ currentColor })
    console.log('获取当前的颜色'+[currentColor,angle])
    //滑动结束 发送当前颜色值
    if (moving === false) {
      this.props.onColorChange(hsv)
    }
  }

注意
1、因为里面用到了RGBHSV的转化,所以我们需要导入"colorsys": "^1.0.17",这个库
2、里面的色盘是标准色盘图片

效果图

在这里插入图片描述

完整代码地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值