美文网首页
React-Native 色盘的实现

React-Native 色盘的实现

作者: 疯狂一生 | 来源:发表于2020-12-25 17:48 被阅读0次

    最近因在学习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、里面的色盘是标准色盘图片

    效果图
    在这里插入图片描述

    原文地址https://editor.csdn.net/md/?articleId=111683437

    完整代码地址: https://download.csdn.net/download/qq_30963589/13781232

    相关文章

      网友评论

          本文标题:React-Native 色盘的实现

          本文链接:https://www.haomeiwen.com/subject/ghjinktx.html