react-native拖拽體驗(yàn)優(yōu)化

this.panResponder = PanResponder.create({
  /***************** 要求成為響應(yīng)者 *****************/
  // 單機(jī)手勢是否可以成為響應(yīng)者
  onStartShouldSetPanResponder: (evt, gestureState) => true,
  // 移動手勢是否可以成為響應(yīng)者
  onMoveShouldSetPanResponder: (evt, gestureState) => true,
  // 攔截子組件的單擊手勢傳遞,是否攔截
  onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
  // 攔截子組件的移動手勢傳遞,是否攔截
  onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
  /***************** 響應(yīng)者事件回調(diào)處理 *****************/
  // 單擊手勢監(jiān)聽回調(diào)
  onPanResponderGrant: (e, gestureState) => {
    console.log('onPanResponderGrant==>' + '單擊手勢申請成功,開始處理手勢');
    this._onPanResponderGrant(e);
  },
  // 移動手勢監(jiān)聽回調(diào)
  onPanResponderMove: throttle((e, gestureState) => {
    console.log('onPanResponderMove==>' + '移動手勢申請成功,開始處理手勢' + `${gestureState}`);
    this._onPanResponderMove(e, gestureState);
  }, 200),
  // 手勢動作結(jié)束回調(diào)
  onPanResponderEnd: (evt, gestureState) => {
    console.log('onPanResponderEnd==>' + '手勢操作完成了,用戶離開');
    this._onPanResponderEnd(evt);
  },
  // 手勢釋放, 響應(yīng)者釋放回調(diào)
  onPanResponderRelease: (e, gestureState) => {
    // 用戶放開了所有的觸摸點(diǎn),且此時(shí)視圖已經(jīng)成為了響應(yīng)者。
    // 一般來說這意味著一個(gè)手勢操作已經(jīng)成功完成。
    this._onPanResponderRelease(e, gestureState);
    console.log('onPanResponderRelease==>' + '放開了觸摸,手勢結(jié)束');
  },
  // 手勢申請失敗,未成為響應(yīng)者的回調(diào)
  onResponderReject: (e, gestureState) => {
    // 申請失敗,其他組件未釋放響應(yīng)者
    console.log('onResponderReject==>' + '響應(yīng)者申請失敗');
    this._onPanResponderRelease(e, gestureState);
  },
  // 當(dāng)前手勢被強(qiáng)制取消的回調(diào)
  onPanResponderTerminate: e => {
    // 另一個(gè)組件已經(jīng)成為了新的響應(yīng)者,所以當(dāng)前手勢將被取消
    console.log('onPanResponderTerminate==>' + '由于某些原因(系統(tǒng)等),所以當(dāng)前手勢將被取消');
    return true;
  },
  onShouldBlockNativeResponder: (evt, gestureState) => {
    return true;
  }
});
  

需求是多個(gè)可以拖拽的對象所以需要知道每次是拖拽哪個(gè),而調(diào)用方法需要用 {...this.panResponder.panHandlers}解構(gòu)之后得到的json通過props傳遞給組件,使得組件變成響應(yīng)者,打印{...this.panResponder.panHandlers}

[圖片上傳失敗...(image-3e7074-1580929245556)]

多個(gè)拖拽就不能知道是哪個(gè)變成了響應(yīng)者

  1. 不用解構(gòu),給View綁定事件,傳遞index

Animated.View綁定事件,如

<Animated.View
  onResponderMove={(nativeEvent, gestureState) => this.onResponderMove(index, nativeEvent, gestureState}
>

將index作為參數(shù)傳遞過去就知道是哪項(xiàng)作為了響應(yīng)者,但是,參數(shù)中g(shù)estureState是undefined

[圖片上傳失敗...(image-5cb757-1580929245556)]

官網(wǎng)中說:它提供了一個(gè)對觸摸響應(yīng)系統(tǒng)響應(yīng)器的可預(yù)測的包裝。對于每一個(gè)處理函數(shù),它在原生事件之外提供了一個(gè)新的gestureState對象。

所以不用PanResponder.create得不到gestureState對象

2.通過重寫解構(gòu)出來的{...this.panResponder}后的onResponderGrant將index放到state中

List.map(li => (
    <Animated.View
      style={styles.scheduleMoveImgView}
      {...this.panResponder.panHandlers}
      onResponderGrant={e => {
        this.touchY = e.nativeEvent.locationY - 12;
        this.initHeight = ((li.end - li.start) / 1800) * initOneHeight;
        this.dragItem = li;
        this.setState({
          isMove: true,
          movingHeight: this.initHeight,
          dragIndex: index
        });
      }}
    >
    </Animated.View>
))

項(xiàng)目中使用了第二種方法,因?yàn)橥献r(shí)需要得到gestureState對象

[站外圖片上傳中...(image-ff863f-1580929245556)]

<ScrollView
  style={{ flex: 1 }}
  scrollEnabled={!isMove}
  ref={view => {
    this.areaScrollView = view;
  }}
>
    <View style={styles.scheduleList}>
        {
            scheduleList.map((li, index) => ())
        }
    </View>
    <View style={styles.lineWrap}>
        {
            scheduleList.map((li, index) => ())
        }
    </View>
    <View style={styles.scheduleSelectList}>
        {
            scheduleSelectList.map((li, index) => ())
        }
    </View>
</ScrollView>

ScrollView的children有3個(gè)View,scheduleList是0-24小時(shí)的間距,lineWrap是今天線,絕對定位,scheduleSelectList是選中的時(shí)間段,絕對定位。

android中的坑

[圖片上傳失敗...(image-8bd84e-1580929245556)]


[圖片上傳失敗...(image-d90c41-1580929245556)]

在android里面只有1/4的區(qū)域能夠點(diǎn)擊,ios沒事,截圖中綠色和黃色的交界處才能點(diǎn)擊。原來只有圖標(biāo)能點(diǎn)擊(圓圈灰色X,2424),在手機(jī)中點(diǎn)擊體驗(yàn)不好,改成黃色區(qū)域能夠點(diǎn)擊(5050),因?yàn)辄S色刪除按鈕和拖動按鈕都是絕對定位,通過拖動,動態(tài)設(shè)置box(藍(lán)色區(qū)域)的height。

box{
    position: 'absolute',
    top,
    height
}

藍(lán)色區(qū)域外的區(qū)域點(diǎn)擊在android中無效

解決方案

<View> // 青色區(qū)域,top - 25, 高度 + 50
    <View></View> // 內(nèi)部邊框區(qū)域 margin 25
</View>

[圖片上傳失敗...(image-6670c6-1580929245556)]

這個(gè)時(shí)候,按鈕就在區(qū)域內(nèi)了,可以點(diǎn)擊按鈕全部位置了

接下來就遇到第二個(gè)問題

2個(gè)box之間間隔一個(gè)區(qū)域,這個(gè)時(shí)候就不能選擇這個(gè)區(qū)域了,2個(gè)box區(qū)域重疊,上面一個(gè)box的高度向下25,下面一個(gè)box的top向上25,中間的一個(gè)區(qū)域不能點(diǎn)擊

設(shè)置區(qū)域position: 'absolute', zIndex: 100會導(dǎo)致按鈕的3/4能點(diǎn)擊,左下角1/4不能點(diǎn)擊問題

[圖片上傳失敗...(image-11774f-1580929245556)]

解決方案

<View> // 一塊區(qū)域
    <View></View> // 不定位
    <View></View> // 定位(寬度80%)
</View>

解決之后

[圖片上傳失敗...(image-7bd580-1580929245556)]

紅色區(qū)域在andriod能點(diǎn)擊選中

但是在ios中紅色區(qū)域是在青色的內(nèi)部(心中感到ios和anroid都有不同的渲染機(jī)制,都有坑),只有設(shè)置外層的View的zIndex: 100大于青色區(qū)域才行,ios中內(nèi)部View的zIndex是在外層zIndex 的基礎(chǔ)上的。所以方案失敗。(如果設(shè)置外面的View的zIndex: 100,就不能設(shè)置width: 80%, 因?yàn)槊總€(gè)區(qū)域都有一個(gè)下邊框,80%的話下邊框的長度也變成了80%了,可以通過設(shè)置內(nèi)部的View的width為Dimensions.get('window').width*0.8 這時(shí)候的下邊框就是一樣了,最終沒采用這種方案)

最終方案

通過設(shè)置 scheduleList.map((li, index) => ()) 生成不同時(shí)間段的高為0.5的下邊框(絕對定位), 內(nèi)部View展示為80%,效果就是上圖一樣了

rn的體驗(yàn)等級,能用,湊合,流暢,絲滑

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

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