美文网首页
RN-实现抖音切换视频效果

RN-实现抖音切换视频效果

作者: 精神病患者link常 | 来源:发表于2020-09-29 12:43 被阅读0次

    实现效果
    1、上下滑动切换
    2、 左滑动弹出列表,右滑动取消列表
    3、加载更多、刷新

    实现效果

    思路

    <View 手势(上滑动、下滑动、左滑动(弹出RightView))>
      <FlatList 禁止滚动 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实现RightView弹出

    3.下拉刷新,上拉加载

    下拉刷新:第一页的位置,下拉超过30,则显示ListHeaderComponent
    上拉加载:最后一页的位置,上拉超过30,则显示loadingComponent

    RightView 手势

    右滑动:setState left 的值,配合LayoutAnimation实现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 // 当前页
    
          // 根据滑动的距离 或者 滑动的速度且滑动距离大于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)){
                // 纵向重置状态
                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 {
                // 纵向滑动
                // 横向重置状态
                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=()=>{
          // 请求接口数据,刷新
          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}/>
    

    相关文章

      网友评论

          本文标题:RN-实现抖音切换视频效果

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