RN-實現(xiàn)抖音切換視頻效果

實現(xiàn)效果
1、上下滑動切換
2、 左滑動彈出列表,右滑動取消列表
3、加載更多、刷新

實現(xiàn)效果

思路

<View 手勢(上滑動、下滑動、左滑動(彈出RightView))>
  <FlatList 禁止?jié)L動 renderItem的高和寬和屏幕一樣/>
</View>
<RightView 手勢(只判斷右滑動,用于返回)/>

通過滑動的x、y判斷是上下滑動還是左右滑動

1.上下滑動

滑動中:通過 flatList.scrollToOffset 設置flatList的滾動位置
滑動結束:通過判斷滑動的位置 || 滑動的速度 進行切換頁面

  1. 滑動距離超過屏幕的1/4
  2. 滑動結束時間 - 滑動開始時間 < 300 && 滑動距離超過 50

再通過flatList.scrollToIndex設置flatList的滾動位置

2.左滑動,顯示RightView.RightView position:'absolute' left: screenWidth

左滑動過程中setState left 的值,配合LayoutAnimation實現(xiàn)RightView彈出

3.下拉刷新,上拉加載

下拉刷新:第一頁的位置,下拉超過30,則顯示ListHeaderComponent
上拉加載:最后一頁的位置,上拉超過30,則顯示loadingComponent

RightView 手勢

右滑動:setState left 的值,配合LayoutAnimation實現(xiàn)RightView消失

具體代碼

index.,js

class ClassName extends React.Component {

    constructor(props) {
        super(props);
        this.props.navigation.setOptions({ headerShown: false })
        if (Platform.OS === 'android') {
          UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true)
        }
        this.state = {
          list: [1,2,3,4,5],
          cityListLeft:width,
          refreshing:false,
          isLoading:false
      }
      this.startTime = 0 // 滾動開始時間
      this.endTime = 0 // 滾動結束時間 ,用于計算滾動的速度(結束時間 - 開始時間,時間越短,滾動速度越快)
      this.page = 0 // 當前頁

      // 根據(jù)滑動的距離 或者 滑動的速度且滑動距離大于50,判斷是否切換

      // 縱向滾動切換頁面條件一
      this.minSlideDistance = 50 // 滾動時間符合要求時的最小滾動距離
      this.maxSlideTime = 300 // 最大的滾動時間,
      
      // 條件二
      this.minSlideScaleY = 1 / 4 // 最小的滾動比例

      // 橫向滾動顯示頁面
      this.minSlideScaleX = 1 / 4 // 最小的拖動比例

      this.isRefresh = false // 用于判斷是否處于下拉刷新中,減少下拉過程總多次setState
      this.isLoadMore = false // 用于判斷是否處于上拉加載更多中,減少上拉過程總多次setState

      // 初始化時定義,不要在render中定義
      this.viewabilityConfig = {viewAreaCoveragePercentThreshold: 80}

      this.panResponder = PanResponder.create({
        // 要求成為響應者,防止彈出的頁面中按鈕不可點擊
       onStartShouldSetPanResponder: (evt, gestureState) => {
          if (Math.abs(gestureState.dx) > 10 || Math.abs(gestureState.dy) > 10){
            return true
          }
          return false
        },
        onStartShouldSetPanResponderCapture: (evt, gestureState) => {

          if (Math.abs(gestureState.dx) > 10 || Math.abs(gestureState.dy) > 10){
            return true
          }
          return false
        },
        onMoveShouldSetPanResponder: (evt, gestureState) => {
          if (Math.abs(gestureState.dx) > 10 || Math.abs(gestureState.dy) > 10){
            return true
          }
          return false
        },
        onMoveShouldSetPanResponderCapture: (evt, gestureState) => {

          if (Math.abs(gestureState.dx) > 10 || Math.abs(gestureState.dy) > 10){
            return true
          }
          return false
        },
        onPanResponderGrant: (evt, gestureState) => {
          // 滑動開始時間
          this.startTime = evt.nativeEvent.timestamp
        },
        onPanResponderMove: (evt, gestureState) => {
          let y = gestureState.dy
          let x = gestureState.dx

          if (Math.abs(x) > Math.abs(y)){
            // 橫向滑動
            if (Math.abs(x) > 30){
              if (x > 0){
                // 向右
                if (this.state.cityListLeft == width){
                }else {
                  this.setState({
                    cityListLeft:x
                  })
                }
              }else {
                // 向左
                if (this.state.cityListLeft == 0){
                }else {
                  x = Math.abs(x)
                  this.setState({
                    cityListLeft:width - x
                  })
                }
              }
            }
          }else {
            // 縱向滑動
            if (Math.abs(y) > 30){
              if (y > 0) {
                // 第一頁,下拉加載更多
                if (this.page == 0){
                  if (!this.isRefresh){
                    this.setState({
                      refreshing:true,
                      isLoading:true
                    })
                    this.isRefresh = true
                    // 請求接口
                    this.onRefresh()
                  }
                  this.flatList.scrollToOffset({animated: false,offset: -y})
                }else {
                  this.flatList.scrollToOffset({animated: false,offset: this.page*height-y})
                }
              } else{
                y = Math.abs(y)
                if (this.page == this.state.list.length - 1){
                  // 最后一頁,上拉刷新
                  if (!this.isLoadMore){
                    this.setState({
                      isLoading:true,
                    })
                    this.isLoadMore = true
                    this.onEndReached()
                  }
                }
                this.flatList.scrollToOffset({animated: false, offset: this.page * height + y})
              }
            }
          }
        },
        onPanResponderRelease: (evt, gestureState) => {
          this.endTime = evt.nativeEvent.timestamp
          let y = gestureState.dy
          let x = gestureState.dx
          if (Math.abs(x) > Math.abs(y)){
            // 縱向重置狀態(tài)
            this.flatList.scrollToIndex({ animated: true, index: this.page });
            // 橫向滑動
            if (x > 0){
              if (this.state.cityListLeft == width){
              }else {
                if (x > width * this.minSlideScaleX){
                  this.setState({
                    cityListLeft:width
                  })
                }else {
                  this.setState({
                    cityListLeft:0
                  })
                }
                LayoutAnimation.easeInEaseOut();
              }
            }else {
              if (this.state.cityListLeft == 0){
              }else {
                if (Math.abs(x) > width * this.minSlideScaleX){
                  this.setState({
                    cityListLeft:0
                  })
                }else {
                  this.setState({
                    cityListLeft:width
                  })
                }
                LayoutAnimation.easeInEaseOut();
              }
            }
          }else {
            // 縱向滑動
            // 橫向重置狀態(tài)
            this.setState({
              cityListLeft:width
            })
            if (y > 0) {
              // 向上
              if (y > height * this.minSlideScaleY || (this.endTime - this.startTime < this.maxSlideTime && y > this.minSlideDistance)) {
                if(this.page != 0){
                  this.page -= 1
                }
              }
              this.flatList.scrollToIndex({ animated: true, index: this.page });
            } else if (y < 0) {
              // 向下
              y = Math.abs(y)
              if (y > height * this.minSlideScaleY || (this.endTime - this.startTime < this.maxSlideTime && y > this.minSlideDistance)) {
                if(this.state.list.length != this.page + 1){
                  this.page += 1
                }
              }
              this.flatList.scrollToIndex({ animated: true, index: this.page });
            }
          }
        },
        onPanResponderTerminate: (evt, gestureState) => {
        },
        onShouldBlockNativeResponder: (evt, gestureState) => {
          return false;
        },
      });

      this.cityPanResponder = PanResponder.create({
        // 要求成為響應者:
        onStartShouldSetPanResponder: (evt, gestureState) => true,
        onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
        onMoveShouldSetPanResponder: (evt, gestureState) => true,
        onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,

        onPanResponderGrant: (evt, gestureState) => {
        },
        onPanResponderMove: (evt, gestureState) => {
          let x = gestureState.dx
            // 橫向滑動
            if (Math.abs(x) > 30){
              if (x > 0){
                // 向右
                this.setState({
                  cityListLeft:x
                })
              }
            }
        },
        onPanResponderRelease: (evt, gestureState) => {
          this.endTime = evt.nativeEvent.timestamp
          let x = gestureState.dx
          // 橫向滑動
          if (x > 0){
            console.log('向右');
            if (x > width * this.minSlideScaleX){
              this.setState({
                cityListLeft:width
              })
            }else {
              this.setState({
                cityListLeft:0
              })
            }
            LayoutAnimation.easeInEaseOut();
          }
        },
        onPanResponderTerminate: (evt, gestureState) => {
        },
        onShouldBlockNativeResponder: (evt, gestureState) => {
          return false;
        },
      });
    }

    componentWillUnmount() {

    }

    componentDidMount(){
   
    }
    onEndReached=()=>{
      console.log('onEndReached');
      setTimeout(() => {
        this.setState({
          isLoading:false,
        })
        this.isLoadMore = false
      }, 1500);
    }

    onRefresh=()=>{
      // 請求接口數(shù)據(jù),刷新
      setTimeout(() => {
        this.setState({
          refreshing:false,
          isLoading:false
        })
        this.isRefresh = false
      }, 1500);
    }
    // 不要在render中定義
    onViewableItemsChanged=(info)=>{
      console.log('infoinfoinfoinfo===',info);
      if(info.viewableItems.length > 0){
        // 顯示了新的item
      }
    }

    render() {
        return Render.render.call(this);
    }
}

const mapStateToProps = state => {
  return {
    userInfo: state.userInfo,
    globalInfo: state.globalInfo,
  }
};

const mapDispatchToProps = dispatch => ({
    getUserInfo: userInfo => dispatch({type: ActionTypes.GET_USERINFO, payload: {userInfo}}),
    loginSuccess: globalInfo => dispatch({type: ActionTypes.LOGIN_SUCCESS, payload: {globalInfo}}),
    setWalletMine: wallet => dispatch({type: ActionTypes.SET_WALLET_MINE, payload: wallet}),

});
export default connect(mapStateToProps, mapDispatchToProps)(FishpondList);

render.js

<View style={styles.mainView} {...this.panResponder.panHandlers}>
  <FlatList   ref={ref=>this.flatList = ref}
              data={this.state.list}
              renderItem={({ item, index }) => {
                return <View key={index}>
                  <View style={[styles.item,{
                    backgroundColor:`rgb(${Math.random() * 255},${Math.random() * 255},${Math.random() * 255})`
                  }]}>
                    <Text style={{color:'#fff',fontSize:70}}>{index+1}</Text>
                  </View>
                </View>
              }}
              ListHeaderComponent={this.state.refreshing ?<ListHeaderComponent/> : <View/>}
              getItemLayout={(data, index) => (
                { length: styles.item.height, offset: styles.item.height * index, index }
              )}
              scrollEnabled={false}
              onViewableItemsChanged={this.onViewableItemsChanged}
              viewabilityConfig={this.viewabilityConfig}
              showsVerticalScrollIndicator={false}
            />
</View>
<RightView panResponder={this.cityPanResponder} left={this.state.cityListLeft}/>
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

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