uniapp 小程序实现类似抖音的简易刷视频功能

uniapp 小程序实现类似抖音的简易刷视频功能

  1. 先上视频

20240725-163843

  1. 直接上代码

    1. 代码中使用的是 uniapp 组件 swiper slider
      slider
      swiper

      1. 代码中使用的 <tab-bar></tab-bar> 组件 是我自定义的组件 可以删除
      2. 代码中的图片地址以及视频地址请更换为自己的路径
javascript 复制代码
<template>
	<view>
		<view class="uni-padding-wrap">
			<view class="page-section swiper">
				<view class="page-section-spacing">
					<swiper class="swiper" @change="changefun" @animationfinish="animationfinishfun" :current="index_"
						:vertical="true">
						<swiper-item v-for="(item,index) in videoList">
							<view class="swiper-item" @click.stop="playPause(index)">
								<image src="https://xxxxxxxxxxxxxx.com/2021083111362225433.png" v-if="!isPlay" class="btn play">
								</image>
								<image src="https://xxxxxxxxxxxxxx.com/2021083111373735978.png" v-if="isPlay&&firstclick"
									class="btn pause"></image>
								<video :custom-cache="false" loop="true" class="video" :id="'id'+index" :duration="item.duration"
									:enable-progress-gesture="true" :controls="false" @timeupdate="(e)=>videoUpdate(e,item,index)"
									:src="item.vlogUrl" :show-center-play-btn="false">
								</video>
							</view>
						</swiper-item>
					</swiper>
				</view>
			</view>
			<tab-bar></tab-bar>
		</view>
		<view v-if="is_active">
			<view class="left">
				<view class="left_box">
					<!--   #ifdef MP-ALIPAY   -->
					<view>
					<!--   #endif  -->
						<view class="slider">
							<slider v-model="activeObj.currentTime" @change="sliderChange" @changing="sliderChanging"
								:activeColor="!isPlay || !updateState ?'#ffffff':'#ffffff4D'"
								:block-size="!isPlay || !updateState?14:12" :block-color="!isPlay|| !updateState?'#ffffff':'#6e6f71'"
								backgroundColor="#4f5253"></slider>
						</view>
					</view>
				</view>
			</view>

		</view>
</template>

<script>
	import tabBar from "../../../components/tabBar/tabBar";
	export default {
		components: {
			tabBar
		},
		data() {
			return {
				videoList: [],
				index_: null,
				index: '0',
				firstclick: false,
				is_active: true,
				isPlay: true,
				activeObj: null,
				sending: false,
				contentId: '',
				updateState: true,
				videoContext: null,
			}
		},
		onLoad(option) {
			// 此接口用来查第一个视频详细信息
			// this.$uniApi.dataRequestNoLoading('GET', 'base/app/v1/contentApp/getDefaultVlogId').then(res => {
			// 我在此处替换为假数据
			let res = {
				"code": 1,
				"data": {
					"id": "1806517903331479553",
					"title": "一日游",
					"subtitle": null,
					"coverImage": "https://xxxxxxxxxxxxxx.com/171954161525350063.jpg", // 请更换为自己的视频
					"readingSum": "239",
					"rankingInclude": null,
					"includeContain": null,
					"vlogUrl": "https://xxxxxxxxxxxxxx.com/2024062810390955101.mp4", // 请更换为自己的图片
					"type": 9,
					"salesNum": null,
					"marketPrice": null,
					"coverImageHeight": 702,
					"coverImageWeight": 1080,
					"headImgUrl": "https://xxxxxxxxxxxxxx.com/171954159983539103.png", // 请更换为自己的图片
					"author": "一日游",
					"isTopping": "0"
				},
				"msg": "成功",
				"success": true
			}
			let id = res.data.id || ""
			this.contentId = id
			let obj = Object.assign({}, {
				id: id
			})
			obj['istrue'] = false
			obj['contentId'] = id
			obj['currentTime'] = 0
			//  用来计算滚动条
			obj['duration'] = 0
			// 视频链接
			obj['vlogUrl'] = res.data.vlogUrl
			// 
			this.videoList.push(obj)
			// 查找视频列表   我的接口是根据当前的视频 id 去查后面的视频
			// 一次查的太多会导致卡顿
			this.oprateVideoList(this.contentId)
			// 初始化第一个视频 并播放
			this.$nextTick(function() {
				let videoContext = uni.createVideoContext(`id0`)
				videoContext.play()
			})
			// })

		},
		watch: {
			index_(val) {
				this.videoContext = uni.createVideoContext(`id${this.index_}`)
				this.contentId = this.videoList[val]['contentId']
			},
			activeObj(val, oldval) {
				if (!oldval) {
					this.$nextTick(function() {
						let videoContext = uni.createVideoContext(`id${this.index_}`)
						videoContext.play()
					})
				}
			}
		},
		methods: {
			// current 改变时会触发 change 事件,event.detail = {current: current, source: source}
			// 暂停当前的视频播放
			changefun(e) {
				this.is_active = false
				let videoContext = uni.createVideoContext('id' + this.index_)
				videoContext.pause()
			},
			playPause(current) {
				// 点击视频时  如果播放就暂停 如果是暂停就开始播放
				let videoContext = uni.createVideoContext('id' + current)
				this.firstclick = true
				if (this.isPlay) {
					videoContext.pause()
					this.isPlay = false
				} else {
					videoContext.play()
					this.isPlay = true
				}
			},
			// 	动画结束时会触发 animationfinish 事件,event.detail = {current: current, source: source}
			animationfinishfun(e) {
				let that = this
				this.isPlay = true
				// 获取下标
				let current = e.detail.current
				this.activeObj = this.videoList[current]
				this.index_ = current
				this.is_active = true
				this.videoList[current]['istrue'] = true
				// 获取当前的 video 标签 并暂停播放
				let videoContext = uni.createVideoContext('id' + this.index_)
				videoContext.pause()
				// 初始化新的video 并播放
				videoContext = uni.createVideoContext('id' + current)
				videoContext.play()
				//  视频滑到最后一个 就再去请求新的 视频列表
				if (this.videoList.length > 0 && current + 1 == this.videoList.length && !this.sending) {
					this.oprateVideoList(this.videoList[current]['contentId'])
				}
			},

			videoUpdate(e, item, index) {
				// 此处用来计算进度条
				if (this.updateState) {
					// #ifdef MP-WEIXIN
					if (e.target.id == 'id' + index) {
						this.videoList[this.index_]['currentTime'] = e.detail.currentTime / e.detail.duration * 100
						this.videoList[this.index_]['duration'] = e.detail.duration
					}
					// #endif
					// #ifdef MP-ALIPAY
					this.videoList[this.index_]['currentTime'] = e.detail.currentTime / e.detail.videoDuration * 100
					this.videoList[this.index_]['duration'] = e.detail.videoDuration
					// #endif
				}
			},
			// 拖动过程中触发的事件,event.detail = {value: value}
			sliderChanging(e) {
				this.updateState = false
			},
			//拖动进度条触发事件
			sliderChange(e) {
				let duration = this.videoList[this.index_]['duration']
				if (duration) {
					this.videoContext.seek(e.detail.value / 100 * duration); //完成拖动后,计算对应时间并跳转到指定位置
					this.videoList[this.index_]['duration'] = e.detail.value
					this.updateState = true
				}
			},
			// 获取视频列表
			oprateVideoList(id) {
				let that = this
				this.sending = true
				// this.$uniApi.dataRequestNoLoading('GET', 'base/app/v1/contentVlog/getVlogListByContentId', {
				// 	contentId: id,
				// }).then(resp => {

				let resp = {
					"code": 1,
					"data": [{
						"contentId": "1606207777364082690",
						"title": "vlog啊",
						"coverImage": "https://xxxxxxxxxxxxxx.com/167178470537619117.png",
						"vlogUrl": "https://xxxxxxxxxxxxxx.com/2022122316383441583.mp4"
					}, {
						"contentId": "1684011338528985090",
						"title": "vlog",
						"coverImage": "https://xxxxxxxxxxxxxx.com/169033454163171300.png",
						"vlogUrl": "https://xxxxxxxxxxxxxx.com/2023072609222991294.mp4"
					}, {
						"contentId": "1678315077972848642",
						"title": "阿达啊",
						"coverImage": "https://xxxxxxxxxxxxxx.com/168897636035107597.png",
						"vlogUrl": "https://xxxxxxxxxxxxxx.com/2023071016074361328.mp4"
					}],
					"msg": "成功",
					"success": true
				}

				resp.data.map(res => {
					res['istrue'] = false
					res['currentTime'] = 0
					res['duration'] = 0
					// return res
				})
				if (that.index_ === null) {
					that.index_ = 0
				}
				that.videoList = that.videoList.concat(resp.data)
				that.activeObj = that.videoList[that.index_]
				that.sending = false
				that.videoList[0]['istrue'] = true
				// })

			}
		},

	}
</script>
<style>
	page {
		background: #000;
	}
</style>
<style scoped lang="scss">
	.circle {
		background: rgba(34, 34, 34, 0.4);
		border-radius: 50%;
		z-index: 2;
		height: 70px;
		width: 70px;
		position: fixed;
		top: 0;

		bottom: 441rpx;
		left: 31rpx;

		margin: auto;

		.red {
			position: absolute;
			top: 0;
			right: 0;
			bottom: 0;
			left: 0;
			margin: auto;
			z-index: 3;
			height: 35px;
			width: 35px;
		}
	}

	.swiper {
		height: 100vh;

		.slider {
			height: 20rpx;
			position: absolute;
			bottom: 67rpx;
			left: 0;
			width: 750rpx;

			slider {
				margin: 0;
			}
		}

		.swiper-item {
			height: 100vh;
			position: relative;


			.btn {
				position: absolute;
				width: 120rpx;
				height: 120rpx;
				background: none;
				z-index: 2;
				top: 50%;
				margin-top: -60rpx;
				left: 50%;
				margin-left: -60rpx;

				&.pause {
					animation: benone .2s 2s linear forwards;
				}
			}

		}
	}

	.video {
		width: 100%;
		height: 100vh;
		position: relative;

	}

	.left_box {
		position: fixed;
		bottom: 55px;
		left: 0rpx;
		width: 100%;
		height: 600rpx;
		pointer-events: none;
		z-index: 8;
		background: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1));

		.slider {
			pointer-events: initial;
			height: 20rpx;
			position: absolute;
			bottom: 67rpx;
			left: 0;
			width: 750rpx;

			slider {
				margin: 0;
			}
		}





		::v-deep .u-icon {
			position: fixed;
			right: 40rpx;
			bottom: 280rpx;
			pointer-events: initial;
			z-index: 10;
		}
	}




	@keyframes benone {
		from {
			opacity: 1;
		}

		to {
			opacity: 0;
		}
	}

	.uni-padding-wrap {
		background-color: #ffffff;
	}
</style>
  1. 已完成!
相关推荐
量子-Alex21 分钟前
【多模态聚类】用于无标记视频自监督学习的多模态聚类网络
学习·音视频·聚类
zhulangfly4 小时前
Wux weapp 组件库的 bug—— wux-picker选择器组件无法正确初始化到选定的value
小程序·wux weapp
mo47764 小时前
Webrtc音频模块(四) 音频采集
音视频·webrtc
icy、泡芙4 小时前
T527-----音频调试
linux·驱动开发·音视频
易我数据恢复大师4 小时前
怎么提取音频保存到本地?电脑音频提取方法
音视频·软件·音频提取
野蛮的大西瓜4 小时前
开源呼叫中心中,如何将ASR与IVR菜单结合,实现动态的IVR交互
人工智能·机器人·自动化·音视频·信息与通信
兔C5 小时前
微信小程序的轮播图学习报告
学习·微信小程序·小程序
用户48062260414156 小时前
使用uniapp开发微信小程序-框架搭建
微信小程序·uni-app
嘟嘟实验室6 小时前
微信小程序xr-frame透明视频实现
微信小程序·ffmpeg·音视频·xr
Cc_Debugger6 小时前
小程序将对象通过url传递到下个页面
小程序