微信小程序 视频列表滑动无限循环(仿抖音)

Posted 老虎帅呆了

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了微信小程序 视频列表滑动无限循环(仿抖音)相关的知识,希望对你有一定的参考价值。

一、写在前面:

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

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

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

先上效果图

 二、开发背景说明

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

三、代码(父级页面)

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

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

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组件)

<wxs module="touch" src="./touch.wxs"></wxs>
<view class="aswiper">
  <view 
    id="aswiper__track" 
    class="aswiper__track" 
    bind:touchstart="touch.touchstart" 
    catch:touchmove="touch.touchmove" 
    bind:touchend  ="touch.touchend" 
    change:trackData="touch.trackDataObserver" 
    trackData="trackData" 
    bind:transitionend="touch.onTransitionEnd"
  >
    <view 
      wx:for="players"
      wx:for-item="player" 
      wx:for-index="idx" 
      wx:key="id" 
      class="aswiper-item aswiper-item--hidden"
    >
      <!-- 有视频 -->
      <view class="aswiper-content" wx:if="player.src">
        <video 
          id="player.id" 
          class="aswiper-item-video" 
          data-player-idx="idx" 
          src="player.src" 
          loop="loop" 
          autoplay="playerIdx == idx ? true :false" 
          object-fit="objectFit" 
          enable-play-gesture="false" 
          enable-progress-gesture="true" 
          show-center-play-btn="false" 
          show-fullscreen-btn="false"
          show-progress="true" 
          controls="true" 
          bindplay="onPlay" 
          bindpause="onPause" 
          bindended="onEnded" 
          binderror="onError" 
          bindtimeupdate="onTimeUpdate" 
          bindwaiting="onWaiting" 
          bindprogress="onProgress" 
          bindloadedmetadata="onLoadedMetaData"
        ></video>
        <!-- 视频覆盖层 -->
        <view class="video-overlay" data-player-idx="idx" bind:tap="onVideoOverlayTap">
          <view class="aswiper-item-panel" hidden="delayShowPanel && !player.scene">
            <default-panel video="curQueue[idx]" player-idx="idx" cur-player-idx="playerIdx"></default-panel>
          </view>
          <!-- 暂停播放按钮 -->
          <image hidden="!player.scene || player.status !== 2" data-player-idx="idx" class="video-play-btn" src="./image/play-btn.png" mode="aspectFit" catch:tap="onVideoPlayBtnTap" />
        </view>
      </view>
      <!-- 无视频展示广告 -->
      <view class="aswiper-content" wx:if="player && !player.src" >
        <ad-custom unit-id="adunit-33e6e9f20e115550" style="height: 100%;"></ad-custom>
      </view>
      <view class="aswiper-content__overlay"></view>
    </view>
  </view>
</view>
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: pause
				src: 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() 
		
	
);

 有问题请留言

仿抖音上下滑动播放视频(兼容安卓,ios,小程序,h5)

仿抖音上下滑动播放视频(兼容安卓,ios,小程序,h5)

运行条件

HBuilder X 2.2.2

安装后,从插件市场导入,即可真机运行

vue

项目地址

github https://github.com/15157757001/scroll-video

uniapp插件市场 https://ext.dcloud.net.cn/plugin?id=664

说明

插件分别用swiper实现(多端兼容)和css3动画实现(暂时只支持app),可自行切换。

插件在uni-app编译模式下编写(已切换)。

默认为weex编译模式,在 manifest.json 的源码视图里配置是切换模式, manifest.json -> app-plus -> nvueCompiler 切换编译模式。

swiper在非App端内嵌video性能比较差,不建议导入过多视频。

项目效果

h5在线地址 https://github.com/15157757001/15157757001

 

以上是关于微信小程序 视频列表滑动无限循环(仿抖音)的主要内容,如果未能解决你的问题,请参考以下文章

仿抖音上下滑动播放视频(兼容安卓,ios,小程序,h5)

仿抖音上下滑动播放视频

仿抖音上下滑动分页视频

flutter开发仿抖音首页面上下滑动切换播放视频效果

微信小程序模仿抖音,全屏播放且有流畅的动画

基于Uni-APP多端「h5+小程序+App」高仿抖音小视频|直播|聊天实例