React-Native 色盤(pán)的實(shí)現(xiàn)

最近因在學(xué)習(xí)RN的混合開(kāi)發(fā),項(xiàng)目中有個(gè)色盤(pán)的需求,難為了好幾天才搞出來(lái),現(xiàn)在記錄下色盤(pán)實(shí)現(xiàn),話不多說(shuō),開(kāi)始敲代碼:

在研究色盤(pán)的繪制之前 我們先來(lái)看下RN中我們使用到的幾個(gè)手勢(shì)參數(shù)

RN手勢(shì)
// 返回值為布爾值, 如果返回值為 true,則表示這個(gè) View 能夠響應(yīng)滑動(dòng)手勢(shì), 兩者有一個(gè)為true即可響應(yīng)
onMoveShouldSetPanResponder: (e, gestureState) => {...}
onMoveShouldSetPanResponderCapture: (e, gestureState) => {...}

// 返回值為布爾值, 如果返回值為 true,則表示這個(gè) View 能夠響應(yīng)觸摸手勢(shì), 兩者有一個(gè)為true即可響應(yīng)
onStartShouldSetPanResponder: (e, gestureState) => {...}
onStartShouldSetPanResponderCapture: (e, gestureState) => {...}

onPanResponderRelease: (e, gestureState) => {...}
// 用戶滑動(dòng)手指時(shí),調(diào)用該方法
onPanResponderMove: (e, gestureState) => {...}

// 返回值為布爾值, 如果返回值為 true,則表示這個(gè) View 能夠響應(yīng)觸摸手勢(shì)
onStartShouldSetResponder={(evt) => {}
// 手勢(shì)點(diǎn)擊時(shí)候的事件
onResponderRelease={(evt) => {}
參數(shù)event(e)

獲取觸摸的位置在被響應(yīng)的 View 中的相對(duì)坐標(biāo),evt.nativeEvent.locationX
nativeEvent
changedTouches - 在上一次事件之后,所有發(fā)生變化的觸摸事件的數(shù)組集合(即上一次事件后,所有移動(dòng)過(guò)的觸摸點(diǎn))
identifier - 觸摸點(diǎn)的ID
locationX - 觸摸點(diǎn)相對(duì)于父元素的橫坐標(biāo)
locationY - 觸摸點(diǎn)相對(duì)于父元素的縱坐標(biāo)
pageX - 觸摸點(diǎn)相對(duì)于根元素的橫坐標(biāo)
pageY - 觸摸點(diǎn)相對(duì)于根元素的縱坐標(biāo)
target - 觸摸點(diǎn)所在的元素ID
timestamp - 觸摸事件的時(shí)間戳,可用于移動(dòng)速度的計(jì)算
touches - 當(dāng)前屏幕上的所有觸摸點(diǎn)的集合

gestureState對(duì)象

stateID -- 觸摸狀態(tài)的ID。在屏幕上有至少一個(gè)觸摸點(diǎn)的情況下,這個(gè)ID會(huì)一直有效。
moveX - 最近一次移動(dòng)時(shí)的屏幕橫坐標(biāo)
moveY - 最近一次移動(dòng)時(shí)的屏幕縱坐標(biāo)
x0 - 當(dāng)響應(yīng)器產(chǎn)生時(shí)的屏幕坐標(biāo)
y0 - 當(dāng)響應(yīng)器產(chǎn)生時(shí)的屏幕坐標(biāo)
dx - 從觸摸操作開(kāi)始時(shí)的累計(jì)橫向路程
dy - 從觸摸操作開(kāi)始時(shí)的累計(jì)縱向路程
vx - 當(dāng)前的橫向移動(dòng)速度
vy - 當(dāng)前的縱向移動(dòng)速度
numberActiveTouches - 當(dāng)前在屏幕上的有效觸摸點(diǎn)的數(shù)量

代碼實(shí)現(xiàn):

書(shū)寫(xiě)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,
                        }]}>
           // 色盤(pán)
          <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是圖標(biāo)的白色底部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): 這個(gè)點(diǎn)擊事件是點(diǎn)擊色盤(pán)的時(shí)候,定位圖標(biāo)的位置
2、{...this._panResponder.panHandlers}: 監(jiān)視器與監(jiān)視區(qū)域關(guān)聯(lián),把圖標(biāo)和手勢(shì)關(guān)聯(lián)起來(lái)

定義手勢(shì)
UNSAFE_componentWillMount() {
    // 圖標(biāo)的手勢(shì)
    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)
          // 記錄滑動(dòng)前 圖表的位置
          this.lastX = this.state.offset.x;
          this.lastY = this.state.offset.y;
        },
      });

  }
關(guān)鍵的數(shù)據(jù)處理
handleTapData(evt,) {
    console.log('開(kāi)始點(diǎn)擊',[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('開(kāi)始滑動(dòng)',[this.lastX,this.lastY])
      
      //此時(shí)視圖的坐標(biāo)
      let locationX = this.lastX +  ges.dx + this.state.thumbSizeW / 2
      let locationY = this.lastY + ges.dy + this.state.thumbSizeW / 2
      // 獲取在父元素的實(shí)際坐標(biāo)點(diǎn)值
      let result = this.getPointValues(locationX,locationY)
      let maxX = result.maxX
      let maxY = result.maxY
      // 獲取此點(diǎn)的顏色值
      this.updateColor(locationX,locationY,moving)

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

  }

  // 獲取實(shí)際坐標(biāo)的 角度 坐標(biāo)值 步長(zhǎng)
  getPointValues(locationX,locationY) {
    //半徑
    let radius = this.state.radius
    // 求斜邊的長(zhǎng)度
    let offsetX = Math.abs(radius - locationX)
    let offsetY = Math.abs(radius  - locationY)
    let length = Math.sqrt(offsetX * offsetX + offsetY * offsetY)// 斜邊長(zhǎng)度

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


    // console.log('是否出界'+[locationX,locationY,angle])
    // 最終的坐標(biāo)
    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
    // 此時(shí)的角度是從左邊為 0°開(kāi)始 順時(shí)針 到右邊為 180° 逆時(shí)針到最右邊為 -180° 
    let angle = Math.atan2(y, x) * (180 / Math.PI)
    // 此轉(zhuǎn)換為左邊 0° 順時(shí)針轉(zhuǎn)一圈為 360°
    angle = 180 - angle 
    console.log('獲取角度'+angle)
    return angle
  }

 // 根據(jù)顏色值更新圖標(biāo)的位置 color : #ffffff
  updateUIWithColor(color) {
    const {radius} = this.state
    // 根據(jù)初始的顏色值回去圖標(biāo)的坐標(biāo)
    const hsv = colorsys.hex2Hsv(color)
    console.log('初始值顏色'+[hsv.h,hsv.s,hsv.v])
    
    //角度
    let angle = 180 - hsv.h
    // 在色盤(pán)中的步長(zhǎng)
    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
    };
  } 
 

 // 根據(jù)坐標(biāo)的位置 獲取顏色值
  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('獲取當(dāng)前的顏色'+[currentColor,angle])
    //滑動(dòng)結(jié)束 發(fā)送當(dāng)前顏色值
    if (moving === false) {
      this.props.onColorChange(hsv)
    }
  }

注意
1、因?yàn)槔锩嬗玫搅?code>RGB和HSV的轉(zhuǎn)化,所以我們需要導(dǎo)入"colorsys": "^1.0.17",這個(gè)庫(kù)
2、里面的色盤(pán)是標(biāo)準(zhǔn)色盤(pán)圖片

效果圖
在這里插入圖片描述

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容