17_微信小程序之抖音微视无限滑动视频列表自定义组件编写

微信小程序之抖音微视无限滑动视频列表自定义组件编写

一.先上效果图

在这里插入图片描述

看到上面,你可能首先会想到,使用swiper然后将swiper的circular设置为true,那么,想象一下,假如视频很多的情况下,1千个,甚至上万个的情况下,只要有一个swiper-item,就会渲染一个Video播放器,这样是非常耗费性能的,本文将在这个基础上做优化,大致思路就是,不管我们的数据有多少条,我们都只渲染3个swiper-item,再配合swiper的切换,切换swiper-item的内容实现复用

二.swiper实现循环滑动
<view class="video-swiper-container">
  <swiper class="video-swiper" circular vertical>
    <swiper-item wx:for="{{_videoList}}" wx:key="*this">
      <view class="video_item" style="background:{{index % 3 == 0 ? '#f00' : (index % 3 == 1 ? '#0f0':'#00f')}}">
        {{item.index}}  
      </view>
    </swiper-item>
  </swiper>
</view>
videoList: {
  type: Array,
  value: [],
  observer: function observer() {
    let newVal = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
    let _videoList = newVal.map((item, index) => {
      let result = {
        index: index,
        url: item,
        objectFit: 'contain'
      }
      return result;
    })
    this.setData({
      _videoList: _videoList
    })
  }
}

代码很简单,只是将swiper的circular设置为了true,并且指定滑动方向为垂直方向,至于video标签,咱们先用一个View标签替换

三.第一次初始化

如果数据本身就小于3条,那么直接把传入的数据全部渲染,swiper默认选中第一个即可,如果数据大于3条,需要根据传入的index确定第一次加载时,swiper-item需要渲染的三个数据项,swiper默认选中第二个

  • 如果传入的index是0,那么,swiper依次渲染最后一条,第一条,第二条

在这里插入图片描述

  • 如果传入的index是最后一个,那么依次渲染倒数第二条,最后一条,第一条

在这里插入图片描述

  • 如果传入的index是其他位置的,则依次渲染上一条,当前index,下一条

在这里插入图片描述

<view class="video-swiper-container">
  <swiper class="video-swiper" circular vertical current="{{_videoList.length >= 3 ? 1:playIndex}}">
    <swiper-item wx:for="{{curQueue}}" wx:key="*this">
      <view class="video_item" style="background:{{index % 3 == 0 ? '#f00' : (index % 3 == 1 ? '#0f0':'#00f')}}">
        {{item.index}}  
      </view>
    </swiper-item>
  </swiper>
</view>
videoList: {
  type: Array,
  value: [],
  observer: function observer() {
    let newVal = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
    let _videoList = newVal.map((item, index) => {
      let result = {
        index: index,
        url: item,
        objectFit: 'contain'
      }
      return result;
    })
    this.setData({
      _videoList: _videoList
    })
    this._videoListChange()
  }
},
playIndex: {
  type: Number,
  value: 0
}
_videoListChange: function() {
  let playIndex = this.data.playIndex;
  let curQueue = this.data.curQueue;
  let videoList = this.data._videoList;

  if(curQueue.length <= 0) {
    if (videoList.length >= 3) {
      if (playIndex == 0) {
        curQueue[0] = videoList[videoList.length - 1]
        curQueue[1] = videoList[0]
        curQueue[2] = videoList[1]
      } else if (playIndex == videoList.length - 1) {
        curQueue[0] = videoList[videoList.length - 2]
        curQueue[1] = videoList[videoList.length - 1]
        curQueue[2] = videoList[0]
      } else {
        curQueue[0] = videoList[playIndex - 1]
        curQueue[1] = videoList[playIndex]
        curQueue[2] = videoList[playIndex + 1]
      }
    } else {
      curQueue = videoList;
    }
    this.setData({
      curQueue: curQueue
    })
  }
}
四.配合swiper的切换,切换swiper-item的内容实现复用

当swiper切换完成后,判断是向上滑动切换到下一个,还是向下滑动切换上一个,假如总共有5条数据

  • 向上滑动切换下一个

一开始,第二个swiper-item被选中,实际显示的是第一条数据

第一次向上滑动切换后,此时会切换到第三个swiper-item,实际显示的是第二条数据,只需要将第一个swiper-item显示的内容设置成第三条数据;

再一次向上滑动切换后,此时会切换到第一个swiper-item,实际显示的是第三条数据,只需要将第二个swiper-item显示的内容设置成第四条数据;

再一次向上滑动切换后,此时会切换到第二个swiper-item,实际显示的是第四条数据,只需要将第三个swiper-item显示的内容设置成第五条数据;

再一次向上滑动切换后,此时会切换到第三个swiper-item,实际显示的是第五条数据,只需要将第一个swiper-item显示的内容设置成第一条数据;

  • 向下滑动切换上一个

一开始,第二个swiper-item被选中,实际显示的是第一条数据

第一次向下滑动切换后,此时会切换到第一个swiper-item,实际显示的是第五条数据,只需要将第三个swiper-item显示的内容设置成第四条数据;

再一次向下滑动切换后,此时会切换到第三个swiper-item,实际显示的是第四条数据,只需要将第二个swiper-item显示的内容设置成第三条数据;

再一次向下滑动切换后,此时会切换到第二个swiper-item,实际显示的是第三条数据,只需要将第一个swiper-item显示的内容设置成第二条数据;

再一次向下滑动切换后,此时会切换到第一个swiper-item,实际显示的是第二条数据,只需要将第三个swiper-item显示的内容设置成第一条数据;

<view class="video-swiper-container">
  <swiper class="video-swiper" circular vertical current="{{_videoList.length >= 3 ? 1:playIndex}}" bindchange="onSwiperChange">
    <swiper-item wx:for="{{curQueue}}" wx:key="*this">
      <view class="video_item" style="background:{{index % 3 == 0 ? '#f00' : (index % 3 == 1 ? '#0f0':'#00f')}}">
        {{item.index}}  
      </view>
    </swiper-item>
  </swiper>
</view>
data: {
  curQueue:[],
  _last: -1,
},
  
onSwiperChange: function (event) {
  let _data = this.data;
  let _last = _data._last;
  let curQueue = _data.curQueue;
  let current = event.detail.current;
  let videoList = _data._videoList;

  if (videoList.length >= 3) {
    let diff = current - _last;
    if (diff === 0) return;
    this.data._last = current;
    let direction = diff === 1 || diff === -2 ? 'up' : 'down';
    /

    let curItem = curQueue[current]
    let realIndex = curItem.index

    if (direction == 'up') {
      let change = (current + 1) % 3
      let next = (realIndex + 1) % videoList.length
      curQueue[change] = videoList[next]
    } else {
      let change = (current - 1) < 0 ? 2 : current - 1
      let pre = (realIndex - 1) < 0 ? (videoList.length - 1) : (realIndex - 1)
      curQueue[change] = videoList[pre]
    }

    this.setData({
      curQueue: curQueue
    }) 
  }
}
五.最后把View换成Video,并控制播放即可
<view class="video-swiper-container">
  <swiper class="video-swiper" circular vertical current="{{_videoList.length >= 3 ? 1:playIndex}}" bindchange="onSwiperChange">
    <swiper-item wx:for="{{curQueue}}" wx:key="*this">
      <view class="video_item" style="background:{{index % 3 == 0 ? '#f00' : (index % 3 == 1 ? '#0f0':'#00f')}}">
        <video 
          style="width: 100%; height: 100%;" 
          id="video_{{index}}" 
          loop="{{loop}}" 
          enable-play-gesture
          enable-progress-gesture 
          show-center-play-btn="{{false}}" 
          direction="0" controls="{{true}}" 
          src="{{item.url}}"
          object-fit="{{item.objectFit || 'cover'}}"/>
      </view>
    </swiper-item>
  </swiper>
</view>
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    videoList: {
      type: Array,
      value: [],
      observer: function observer() {
        let newVal = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
        let _videoList = newVal.map((item, index) => {
          let result = {
            index: index,
            url: item,
            objectFit: 'contain'
          }
          return result;
        })
        this.setData({
          _videoList: _videoList
        })
        this._videoListChange()
      }
    },
    playIndex: {
      type: Number,
      value: 0
    },
  },

  lifetimes: {
    attached: function attached() {
      this.data._videoContexts = [wx.createVideoContext('video_0', this), wx.createVideoContext('video_1', this), wx.createVideoContext('video_2', this)];
    }
  },

  /**
   * 组件的初始数据
   */
  data: {
    curQueue:[],
    _last: 1,
    _videoContexts: []
  },

  /**
   * 组件的方法列表
   */
  methods: {
    _videoListChange: function() {
      let playIndex = this.data.playIndex;
      let curQueue = this.data.curQueue;
      let videoList = this.data._videoList;

      if(curQueue.length <= 0) {
        if (videoList.length >= 3) {
          if (playIndex == 0) {
            curQueue[0] = videoList[videoList.length - 1]
            curQueue[1] = videoList[0]
            curQueue[2] = videoList[1]
          } else if (playIndex == videoList.length - 1) {
            curQueue[0] = videoList[videoList.length - 2]
            curQueue[1] = videoList[videoList.length - 1]
            curQueue[2] = videoList[0]
          } else {
            curQueue[0] = videoList[playIndex - 1]
            curQueue[1] = videoList[playIndex]
            curQueue[2] = videoList[playIndex + 1]
          }
        } else {
          curQueue = videoList;
        }
        this.setData({
          curQueue: curQueue
        }, () => {
          this.playCurrent(videoList.length >= 3 ? 1:playIndex);
        }) 
      }
    },

    onSwiperChange: function (event) {
      let _data = this.data;
      let _last = _data._last;
      let curQueue = _data.curQueue;
      let current = event.detail.current;
      let videoList = _data._videoList;

      if (videoList.length >= 3) {
        let diff = current - _last;
        if (diff === 0) return;
        this.data._last = current;
        let direction = diff === 1 || diff === -2 ? 'up' : 'down';
        /

        let curItem = curQueue[current]
        let realIndex = curItem.index

        if (direction == 'up') {
          let change = (current + 1) % 3
          let next = (realIndex + 1) % videoList.length
          curQueue[change] = videoList[next]
        } else {
          let change = (current - 1) < 0 ? 2 : current - 1
          let pre = (realIndex - 1) < 0 ? (videoList.length - 1) : (realIndex - 1)
          curQueue[change] = videoList[pre]
        }

        this.setData({
          curQueue: curQueue
        }, () => {
          this.playCurrent(current);
        }) 
      }
    },

    playCurrent: function playCurrent(current) {
      let curQueue = this.data.curQueue
      this.data._videoContexts.forEach((ctx, index) => {
        if(index !== current) {
          ctx.pause()
          ctx.seek(0)
        } else {
          ctx.play()
        }
      });
    },
  }
})
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值