微信小程序 视频列表滑动无限循环(仿抖音)
创始人
2024-02-28 09:02:13
0

一、写在前面:

1:安卓ios表现基本一致,不是swiper组件实现,滑动效果流畅不卡顿,实现了列表无限循环。不是使用官方的腾讯视频播放组件,完整代码在下面

2:实现功能:支持位置导航、拨打电话、复制微信号、分享、联系客服、解析腾讯视频地址和拖动视频进度功能,暂未添加评论留言功能,后期会逐步增加

3:关于视频存储的问题,这里增加了腾讯视频方式,可以减少自身存储的相关费用及性能问题,这里要说明一下,并非使用腾讯视频播放组件,那个会有几秒的广告,体验太差,这里是直接拿的腾讯视频源播放地址的mp4地址,并且没有腾讯视频的logo,如下图(将视频上传至腾讯视频,然后存储播放页面地址进行解析就可以了)

先上效果图

 二、开发背景说明

微信小程序中,同一个页面最多支持添加三个video组件,所以就通过数据处理的方式更新显示播放,有人说只写一个也行,如果只有一个那么上下滑动到一半位置的时候是看不到下一个视频内容的,衔接效果不好

三、代码(父级页面)

/*
*   navbar是我自己封装的头部导航组件
    avideo-swiper 为视频列表组件
*/
{parameter}}' showTitle="{{showTitle}}" >
  video-list="{{videoList}}" 
  initial-index="{{videoIndex}}" 
  bind:change="onChange" 
  duration="{{duration}}" 
  bind:play="onPlay" 
  bind:wait="onWait"
>

他们官方让多写一些内容,要不然就提示内容不佳,我就把代码拿出来了

const app = getApp();

Page({
  data: {
    showTitle:true,
    parameter: {
      'navbar': '1',
      'return': '1',
      'title': '乡村线报',
      'color': true,
      'class': 'trans',
    },
    url: app.globalData.url,
    videoList: [],
    pageNum: 1,
    pageSize: 5,
    videoIndex: 0, //初始播放第几个
    duration: 500, //滑动切换时常
    dataIdxNow:0,  //当前播放第几个,获取分享内容用的
  },
  onLoad(options) {
    if(options.id){
      this.setData({
        shareId:options.id
      })
      // 获取视频详情
      this.getVideoDetail()

    } else {
      // 获取视频列表
      this.getVideoList()
    }
  },
  // 获取视频列表
  getVideoList() {
    let that = this,
      {
        pageSize,
        pageNum
      } = that.data
    wx.request({
      url: that.data.url + 'xxxxxxx&page=' + pageNum + '&limit=' + pageSize,
      header: {
        'content-type': 'application/json'
      },
      success(res){
        if (res.data.code == 0) {
          let list = res.data.data.list;
          list.push({})
          
          let LoadEnd = res.data.data.page_count <= pageNum ? true : false

          that.setData({
            LoadEnd,
            videoList:[...that.data.videoList, ...list]
          })
        }
      }
    })
  },
  // 获取视频详情
  getVideoDetail() {

    let that = this,shareId = that.data.shareId
    wx.request({
      url: that.data.url + 'xxxxxx&id=' + shareId,
      header: {
        'content-type': 'application/json'
      },
      success(res){
        if (res.data.code == 0) {
          let detail = res.data.data.data

          that.setData({
            videoList:[...[],...[detail]]
          })
        }
        that.getVideoList()
      }
    })
  },
  
  onChange(e) {
    let dataIdx = e.detail.dataIdx,
      {
        videoList,
        LoadEnd
      } = this.data

    // 剩余2个时加载下一页
    if ((dataIdx + 2) == videoList.length) {
      this.setData({
        pageNum: LoadEnd ? 1 : this.data.pageNum + 1
      })

      this.getVideoList()
    }

    this.setData({
      dataIdxNow:dataIdx
    })
  },
  onPlay(e) {
  },
  onWait(e) {
  },

  //分享,这里区分点击的是右上角三个点还是页面分享按钮
  onShareAppMessage (res) {
    let title = '',shareInfo=''

    if(res.from == 'button'){
      shareInfo = res.target.dataset.shareinfo;
      
    } else {
      let {videoList,dataIdxNow} = this.data
      shareInfo = videoList[dataIdxNow]
    }

    title = shareInfo.content
    if(shareInfo.province){
        title = title+"ꔷ"+shareInfo.province+""+shareInfo.position
    }

    return {
      title: title,
      path: '/pages/video_swiper/video-swiper?id='+shareInfo.id,
      imageUrl: shareInfo.pic_url
    }
  },
})

 四、代码(avideo-swiper组件)


{touch.touchstart}}" catch:touchmove="{{touch.touchmove}}" bind:touchend  ="{{touch.touchend}}" change:trackData="{{touch.trackDataObserver}}" trackData="{{trackData}}" bind:transitionend="{{touch.onTransitionEnd}}">{players}}"wx:for-item="player" wx:for-index="idx" wx:key="id" class="aswiper-item aswiper-item--hidden">{player.src}}">{idx}}" bind:tap="onVideoOverlayTap">{delayShowPanel && !player.scene}}">{curQueue[idx]}}" player-idx="{{idx}}" cur-player-idx="{{playerIdx}}">{!player.scene || player.status !== 2}}" data-player-idx="{{idx}}" class="video-play-btn" src="./image/play-btn.png" mode="aspectFit" catch:tap="onVideoPlayBtnTap" />{player && !player.src}}" >
const app = getApp()Component({properties: {vertical: {type: Boolean,value: true},duration: {type: Number,value: 500},videoList: {type: Array,value: []},initialIndex: {type: Number,value: 0},objectFit: {type: String,value: 'contain'},loop: {type: Boolean,value: true},autoPlay: {type: Boolean,value: true},panelType: {type: String,value: 'default'},width: {type: Number,value: 0},height: {type: Number,value: 0}},data: {players: [{id: 'video_0',scene: false,status: 0, // 0: initial; 1: play; 2: pausesrc: null,},{id: 'video_1',scene: false,status: 0,src: null,},{id: 'video_2',scene: false,status: 0,src: null,}],playerIdx: 0,trackData: {width: 0,height: 0,vertical: true,duration: 500,operation: {}},curQueue: [{}, {}, {}],curVideo: null,navH:0,},observers: {initialIndex(index) {if (index < 0) {throw new Error('initialIndex can not be less than 0.');}},videoList(videoList) {if (!Array.isArray(videoList)) {throw new Error('videoList is expected an array.');}},'initialIndex, videoList': function (initialIndex, videoList) {const operation = {};if (initialIndex !== this._initialIndex && videoList.length > 0) {this._initialIndex = initialIndex;this._dataIdx = initialIndex;operation.dataIdx = initialIndex;}operation.dataCount = videoList.length;if (!this._videoList) {this._playing = this.data.autoPlay;}this.setData({'trackData.operation': operation},() => {this.loadCurQueue(this._dataIdx, this._playing);});}},created() {this._rect = null;this._videoList = null;this._initialIndex = -1;this._dataIdx = 0;this._lastDataIdx = -1;this._lastVideo = null;this._playing = true;this._pausing = {idx: -1,timmer: null};this._savedPlayerIdx = -1;this._playerIdx = 0;this._isAndroid = wx.getSystemInfoSync().platform === 'android';},attached() {// 创建视频对象this._videoContexts = [];this.data.players.forEach((item) => {this._videoContexts.push(wx.createVideoContext(item.id, this));})},ready() {this.initialize();},methods: {play() {const { curVideo } = this.data;if (curVideo) {this.playCurrent(this._playerIdx);}},pause() {this._videoContexts.forEach((ctx) => {ctx.pause();});},swiperChange(args) {const dataIdx = args.dataIdx;this._dataIdx = dataIdx;this.loadCurQueue(dataIdx, false);},loadCurQueue(dataIdx, playing = false) {const curQueue = this.data.curQueue.slice(0);const { videoList, players } = this.data;const maxIdx = videoList.length - 1;let curVideo = null;let curDataIdx = dataIdx;let cur = 0;if (maxIdx < 0) {curQueue.forEach((video) => {video = {};});} else {if (curDataIdx > maxIdx) {curDataIdx = maxIdx;}let preV = {},nextV = {};let pre = 0,next = 0;cur = curDataIdx % 3;pre = cur - 1;if (pre < 0) {pre = 2;}next = cur + 1;if (next > 2) {next = 0;}if (curDataIdx - 1 >= 0) {preV = videoList[curDataIdx - 1];}if (curDataIdx + 1 <= maxIdx) {nextV = videoList[curDataIdx + 1];}curQueue[pre] = preV;curQueue[next] = nextV;curVideo = videoList[curDataIdx];curQueue[cur] = curVideo;curVideo = videoList[curDataIdx];}for (let i = 0; i < 3; i++) {const video = curQueue[i];const player = players[i];const src = video.url || null;player.src = src;}this.setData({players,curQueue,curVideo});this._playerIdx = cur;this._savedPlayerIdx = -1;if (curVideo) {this._videoList = videoList;if (curDataIdx !== this._lastDataIdx) {this._lastDataIdx = curDataIdx;this.triggerEvent('change', {dataIdx: curDataIdx,})}this._lastVideo = curVideo;if (playing && curVideo) {wx.nextTick(() => {this._savedPlayerIdx = cur;this.playCurrent(cur);});}}},// 点击暂停播放onVideoOverlayTap(e) {const idx = e.currentTarget.dataset.playerIdx;const ctx = this._videoContexts[idx];const player = this.data.players[idx];if (player.status === 2) {if (player.src) {ctx.play();}} else {ctx.pause();const status = `players[${idx}].status`;const scene = `players[${idx}].scene`;this.setData({[status]: 2,[scene]: true});}},// 点击开始播放onVideoPlayBtnTap(e) {const idx = e.currentTarget.dataset.playerIdx;const ctx = this._videoContexts[idx];const player = this.data.players[idx];if (player.src) {ctx.play();}},onPlay(e) {const idx = e.currentTarget.dataset.playerIdx;const player = this.data.players[idx];const _pausing = this._pausing;const lastStatus = player.status;this._playing = true;if (idx === _pausing.idx) {clearTimeout(_pausing.timmer);this._pausing = {idx: -1,timmer: null};}if (lastStatus !== 1) {const scene = `players[${idx}].scene`;const status = `players[${idx}].status`;this.setData({[scene]: true,[status]: 1});if (lastStatus === 2) {this.trigger(e, 'replay');} else {this.trigger(e, 'play');}}},onPause(e) {const idx = e.currentTarget.dataset.playerIdx;const player = this.data.players[idx];this._playing = false;if (player.status !== 2) {const status = `players[${idx}].status`;this._pausing = {idx,timmer: setTimeout(() => {this.setData({[status]: 2});this._pausing = {idx: -1,timmer: null};}, 200)};}this.trigger(e, 'pause');},onEnded(e) {this.trigger(e, 'ended');},onError(e) {this.trigger(e, 'error');},onTimeUpdate(e) {this.trigger(e, 'timeupdate');},onWaiting(e) {this.trigger(e, 'wait');},onProgress(e) {this.trigger(e, 'progress');},onLoadedMetaData(e) {this.trigger(e, 'loadedmetadata');},trigger(e, type) {let ext = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};let detail = e.detail;const { curVideo } = this.data;this.triggerEvent(type, Object.assign(Object.assign({}, detail), { video: curVideo }, ext));},playCurrent(cur) {const { players } = this.data;this._videoContexts.forEach((ctx, idx) => {const player = players[idx];if (cur === idx) {if (player.src) {ctx.play();}} else {player.scene = false;player.status = 0;ctx.stop();}});this.setData({playerIdx: cur,players});},onTransitionEnd() {const { curVideo } = this.data;if (this._playerIdx !== this._savedPlayerIdx) {if (curVideo) {this._savedPlayerIdx = this._playerIdx;this.playCurrent(this._playerIdx);}}},initialize() {this.getRect('#aswiper__track').then((rect) => {const { width, height } = this.data;this._rect = rect;this.setData({'trackData.width': width,'trackData.height': height,'trackData.operation': {rect}});});},getRect(selector, all) {var _this = this;return new Promise(function (resolve) {wx.createSelectorQuery().in(_this)[all ? 'selectAll' : 'select'](selector).boundingClientRect(function (rect) {if (all && Array.isArray(rect) && rect.length) {resolve(rect);}if (!all && rect) {resolve(rect);}}).exec();});},noop() {}}
});

 有问题请留言

相关内容

热门资讯

《全民阅读促进条例》将施行,多... 一纸条例,用法治护航全民阅读;满城书香,焕发青春中国活力。近日,《全民阅读促进条例》(以下简称《条例...
原创 戴... 最近,关于前国脚戴琳的欠薪丑闻无疑是引发了球迷的持续关注,从10月25日,媒体人李平康率先爆料,晒出...
思想政治工作条例最新修订内容,... 思想政治工作条例最新修订内容,思想政治工作条例全文下载 思想政治工作条例最新修订,全文下载与深度解读...
CBA潜力赛为何打成“老将赛”... 计时钟归零,双方教练握手致意,观众开始退场,CBA联赛的正赛宣告结束。然而球场并未就此沉寂,替补席上...
“手术钻头断裂遗留患者体内”,... 12月21日,湖南祁阳市卫生健康局发布情况通报称,近日,有媒体报道祁阳市中医医院发生骨科手术钻头断裂...
代驾纠纷 代驾时撞伤行人、车辆发生故障…… 这些都和车主无关,应由代驾赔偿? 观点: 使用代驾服务并非将所有...
公司股东与妻子分居期间出轨女下... 近日据报道,宁夏永宁县人民法院一审查明公司股东李某乙在与妻子李某甲分居期间,与公司女员工马某某存在不...
动物学家、律师和创作者,Thi... 12月21日,以“一起·了不起”为主题的2025 ThinkPad黑FUN礼在京举办。活动现场,律师...
徐奇渊:扩内需与对外政策紧密相... 近日,中国海关总署发布了一组数据令人关注:2025年前11个月,我国货物贸易顺差达到1.08万亿美元...