React Native 无限循环轮播思路一

对于React Native,我想说入坑需谨慎

背景

最近做项目中有一个类似今日头条小视频左右滑动可以切换小视频的需求。对于这个需求如何实现,我首先想到的是用FlatList去解决,但是FlatList扩展性很差,不太适合。然后我想到了VirtualizedList去实现,想了想还是很麻烦,项目很急,自己去一点点写来不及。如果是用ScrollView去实现此功能,倒是比较容易,但是考虑到列表的数据量可能是成百上千条数据,即使再优化,数据量一多,App肯定卡的动不了。后来我发现react-native-swiper这个库,它有针对左右滑动长列表的优化,尝试了一下还可以,然后就使用了。功能很快完成了,但是当数据量达到200条以后,就明显感觉到卡顿了,App上线后用户反馈并不好。既然这些组件都不能很好的解决长列表的问题,那我自己写一个滑动组件。

效果

效果图

思路

1、每次展示列表中的三条数据
2、三条数据插入方式如图,其实是5条数据,第一条和最后一条分别为第三条数据和第一条数据(随便一画有点难看):


图片插入方式

3、每一条数据都为屏幕宽度"const {width} = Dimensions.get('window')",总宽度度为5倍宽度"width * 5",当然这个宽度可以自定义。
4、首先展示第一条数据(数字为1的数据),若向做滑动到最后为1条数据的时候,在动画完成后,将位置重置为数字为1的地方,这样就实现了左滑功能,右滑动反之。
5、需要是用手势PanResponder与动画Animated,来实现滑动拖拽与动画效果

代码

import React, {Component} from 'react';
import {View, Animated, Dimensions, PanResponder, Image} from 'react-native';

const { width } = Dimensions.get('window')

class SwiperView extends Component {
  constructor(props){
    super(props);
    this.state={
      sports: new Animated.Value(-width), // 设置初始值
    }
    this.startTimestamp = 0 // 拖拽开始时间戳(用于计算滑动速度)
    this.endTimestamp = 0 // 拖拽结束时间戳用于计算滑动速度)
    this.page = 1 // 首次展示第一条数据(page 最小值为0,即从0开始,1为第二个条目)
  }
  componentWillMount () {
    this.panResponder()
  }

  panResponder () {
    this._panResponder = PanResponder.create({
      onMoveShouldSetPanResponder: (evt, gestureState) => true,
      onPanResponderTerminationRequest: (evt, gestureState) => true,
      onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
      onPanResponderGrant: (evt, gestureState) => {
        // 滑动开始,记录时间戳
        this.startTimestamp = evt.nativeEvent.timestamp
      },
      onPanResponderMove: (evt, gestureState) => {
        // 滑动横向距离
        let x = gestureState.dx
        // 实时改变滑动位置
        if (x > 0) {
          this.setState({
            sports: new Animated.Value(-this.page * width + x)
          })
        } else {
          this.setState({
            sports: new Animated.Value(x - this.page * width)
          })
        }
      },
      onPanResponderRelease: (evt, gestureState) => {
        // 滑动结束时间戳
        this.endTimestamp = evt.nativeEvent.timestamp
        // 滑动距离,根据滑动距离与时间戳计算是否切换到下一个条目
        let x = gestureState.dx
        if (x > 0) {
          // 滑动距离大于屏幕1半,开启动画,滑动到下一个界面,或者滑动速度很快,并且滑动距离大于20,也滑动到下一个条目
          if (x > width / 2 || (this.endTimestamp - this.startTimestamp < 300 && x > 20)) {
            this.page -= 1
          }
          Animated.timing(
              this.state.sports,
              {
                  toValue: -this.page * width,
                  duration: 200
              }
          ).start((state) => {
            // 动画完成,判断是否需要重置位置
            if (state.finished) {
              if (this.page <= 0) {
                this.page = 3
                this.setState({
                  sports: new Animated.Value(-3 * width)
                })
              }
            }
          });
        } else {
          x = Math.abs(x)
          // 滑动距离大于屏幕1半,开启动画,滑动到下一个界面,或者滑动速度很快,并且滑动距离大于20,也滑动到下一个条目
          if (x > width / 2 || (this.endTimestamp - this.startTimestamp < 300)) {
            this.page += 1
          }
          Animated.timing(
              this.state.sports,
              {
                  toValue: -this.page * width,
                  duration: 200
              }
          ).start((state) => {
            // 动画完成,判断是否需要重置位置
            if (state.finished) {
              if (this.page >= 4) {
                this.page = 1
                this.setState({
                  sports: new Animated.Value(-width * this.page)
                })
              }
            }
          });
        }
      },
      onShouldBlockNativeResponder: (evt, gestureState) => {
        return false
      }
    })
  }
  render(){
    return (
        <Animated.View
          style={{...this.props.style, left:this.state.sports}}
          {...this._panResponder.panHandlers}
        >
            {this.props.children}
        </Animated.View>
    );
  }
}

export default class App extends Component {
  render() {
    return (
      <View style={[{width:width,height:'100%'}]}>
        <SwiperView style={{width:width * 4,height:'100%',flexDirection:"row"}}>
              <Image source={require('./assets/3.jpeg')} style={[{width,height:'100%',backgroundColor:"#FFF"}]} />
              <Image source={require('./assets/1.jpeg')} style={[{width,height:'100%',backgroundColor:"red"}]} />
              <Image source={require('./assets/2.jpeg')} style={[{width,height:'100%',backgroundColor:"green"}]} />
              <Image source={require('./assets/3.jpeg')} style={[{width,height:'100%',backgroundColor:"#FFF"}]} />
              <Image source={require('./assets/1.jpeg')} style={[{width,height:'100%',backgroundColor:"red"}]} />
        </SwiperView>
      </View>
    );
  }
}

总结

1、这只是实现需求的第一步,后续会继续优化、封装,达到想要的效果
2、如果只想做banner轮播图展示,将手势那一块替换为setInterval就可以了。
3、如果有更好的思路欢迎交流

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容

  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    X先生_未知数的X阅读 15,960评论 3 119
  • 从今年五月开始抽空学习心蓝老师的彩铅课,一转眼第八课了。 这个大樱桃画了两次,第一次线稿弄脏了,看着很不舒服,今天...
    vevine阅读 319评论 2 2
  • 谁都是被上帝咬过一口的苹果,我希望你能拥抱我无法摒弃的不完美,是这些短处与长处共有的一个我,才刚好来到你身边。
    瑞妞阅读 219评论 0 0
  • 14天一闪而过,回想起来,依然记得当时那份既忐忑又兴奋的心情。 知道小白训练营,是看到babe的公号推送,正是自己...
    小太阳_6a9e阅读 663评论 1 18
  • 这幅画好有意思呀,你的作画顺序是房子、树木和人,那个蹲下来拍照的是你,据我所知,你现在好像还没有男朋友,那这就是对...
    老孙家的大彬阅读 602评论 1 0