React-Native 使用 ScrollView 简单实现 Banner 功能

前言

遇到一个需求,需要做到一个2行列表的滑动,并且要求有底部的指示器,实际就是一个 banner ,由于原本代码是每行4个,分成2行,无法滚动,现在要在原有基础上改造成一行5个可滚动,故而没有大动代码,只是在外层嵌套 ScrollView ,利用滚动的属性模拟出了 banner 的效果:

React-Native 使用 ScrollView 简单实现 Banner 功能_第1张图片

实现

初始偏移量

方案的主要思路在于 ScrollView 的滚动监听,每次滚动开始之前,通过 onScrollBeginDrag 方法初始化总的偏移量:

onScrollBeginDrag = {event => {
        this.setState({
            totalOffset:0
        })
    }
}

滚动过程

在滚动过程中,滚动开始前onScrollBeginDrag 执行,然后可能有一个或多个 onScroll 执行,拖拽动作结束之后会执行 onScrollEndDrag ,但是此时 onScroll 还可能执行,因为拖拽完成之后页面可能因为惯性继续滑。
React-Native 使用 ScrollView 简单实现 Banner 功能_第2张图片
关于 React-Native 触摸事件的生命周期,这里有一篇文章可以了解下:

https://blog.csdn.net/liu__520/article/details/53676834

偏移量获取

通过 event.nativeEvent.contentOffset.x 拿到当前一个 onScroll 的偏移量,

计算总偏移,刷新UI

因为在 onScrollEndDrag 后,仍旧有 onScroll 事件,故而没有使用 onScrollEndDrag 中判断累计滑动的方式来实现,而是在 onScroll 中计算。

通过拿取当前onScroll中的event.nativeEvent.contentOffset.x叠加到总偏移量中,如果总偏移量(多个 onScroll 偏移量的总和) 超过单个 item宽度或者单个 onScroll 偏移量大于 item宽度,代表滑动到新的页面了,需要更新底部圆点指示器的颜色(通过改变state中curPageIndex的值):

onScroll = { event => {
    // 拿到当前一次滚动的x偏移量
     const currentOffset = event.nativeEvent.contentOffset.x;
      const dif = currentOffset - (this.offset || 0);
      //由于一次滚动可能有多个onScroll事件,所以需要拿到当前总的偏移量,去计算新的总偏移量
      let curTotal = this.state.totalOffset 
      // 一屏有4个item,itemWidth等于1/4的屏幕宽度
      let itemWith = deviceWidth/4
      if (dif < 0) {//小于0,代表往右滑,内容左移,页数-1
          let total = curTotal - dif 
          this.setState({
              totalOffset:curTotal-dif
          })
          //如果一次滑动里,所有的偏移量大于item宽度;或者单个偏移量大于item宽度,则更新页面index
          if (total >= itemWith || currentOffset>=itemWith) {
              this.setState({
                  curPageIndex:0
              })
          }
      } else { //大于0,代表左滑,内容右移,页数+1
          let total = curTotal + dif
          this.setState({
              totalOffset:curTotal+dif
          })
          if (total >= itemWith || currentOffset>= itemWith) {
          this.setState({
              curPageIndex:1
          })
      }
      }
      this.offset = currentOffset;
  }}

完整实现代码

render代码如下:

render() {
    //是否第一页
    let isFirstPage = this.state.curPageIndex === 0
    let list1 = [] // 第一行的item列表
    let list2 = [] // 第二行的item列表
    return (
     <ScrollView style = {{flex:1}}
                    horizontal = {true}
                    onScrollBeginDrag = {event => {
                            this.setState({
                                  totalOffset:0
                            })
                        }
                    }
                    onScroll = { event => {
                        const currentOffset = event.nativeEvent.contentOffset.x;
                        const dif = currentOffset - (this.offset || 0);
                        let curTotal = this.state.totalOffset 
                        let itemWith = deviceWidth/4
                        if (dif < 0) {
                            let total = curTotal - dif 
                            this.setState({
                                totalOffset:curTotal-dif
                            })
                            if (total >= itemWith || currentOffset>=itemWith) {
                                this.setState({
                                   curPageIndex:0
                                })
                            }
                        } else {
                            let total = curTotal + dif
                            this.setState({
                                totalOffset:curTotal+dif
                            })
                            if (total >= itemWith || currentOffset>= itemWith) {
                            this.setState({
                                curPageIndex:1
                            })
                        }
                        }
                        this.offset = currentOffset;
                    }}
                    showsHorizontalScrollIndicator = {false}>
                    <View style = {{flex:1,flexDirection:'column'}}>
                    <View style = {{flex:1,flexDirection:'row',flexWrap:'nowrap'}}>
                    <View style={{flexDirection: 'row',justifyContent:'space-between'}}>
                        {list1.map((item,index) => {
                            //第一排 Icon 和文字的 item
                             return (
                                 <IconItem />
                             )
                        })}
                    View>

                View>
                <View style = {{flex:1,flexDirection:'row',flexWrap:'nowrap'}}>

                    <View style={{flexDirection: 'row',justifyContent:'space-between'}}>
                        {list2.map((item,index) => {
                            return (
                                //第二排 Icon 和文字的 item
                                <IconItem />
                            )
                        })}
                    View> 
                View>
                View>
                ScrollView>
                <View style = {{flex:1,flexDirection:'row',justifyContent:'center',paddingBottom:10}}>
                    <View style = {{width:5,height:5,backgroundColor:isFirstPage ? '#999999':'#f4f4f4',borderRadius:2.5,marginRight:3}}/>
                    <View style = {{width:5,height:5,backgroundColor:isFirstPage ? '#f4f4f4':'#999999',borderRadius:2.5,marginLeft:3}}/>
                View>
                )
}

你可能感兴趣的:(React,Native)