uniapp使用nvue三item写仿抖音短视频

实机效果


首先,代码最主要的思路先说明一下:

        1.本身uniapp的渲染能力并不高,即时用上nvue也不能做到完美,所以我选择只渲染三个video,不过代码方面也还可以优化,这只是我简单的一个demo和思路提供,列如滑动暂停,或者视频先用封面处理。

        2.三个item就需要每次滑动都判断是到底是上还是下。

        3.再滑动到特定的index时需要不同的处理,需要在滑动时提前做好下一个的处理,比如在上滑到第二个播放器时,需要对三个播放器进行处理了。

先创建nvue项目,这边也就不多说了:

先在nvue页面创建一个swiper组件用for循环三个item里嵌套video,使用cover-view和cover-image来使层级在video之上

<template>
	<view style="height: 100vh;flex:1;width:750rpx">
		<swiper @animationfinish="qiehuan" :current="activeIndex" @change="swiper" :circular="index==0?true:false" vertical
			class="list">
			<swiper-item disable-programmatic-animation class="item" v-for="(item,index) in list" :key="index">
				<video @ended="end" @pause='item.active=false' @click="pause(item,index)"
					@timeupdate="long($event,item,index)" :id="'Video'+index" class="video"
					:show-center-play-btn='false' :controls="false"
					src="https://media.w3.org/2010/05/sintel/trailer.mp4">
					<cover-view style="margin-top: 50rpx">
						<text style="color: aqua;">{{item.id}}</text></cover-view>
					<cover-view class="back" v-if="!item.active">
						<cover-image class="pauseIcon" src="../../static/icon/pause.png"></cover-image>
					</cover-view>
					<cover-view class="iconList">
						<cover-view class="iconListClass">
							<cover-image class="icon" src="../../static/icon/like.png"></cover-image>
							<cover-view>
								<text class="context">12.5w</text>
							</cover-view>
						</cover-view>
						<cover-view class="iconListClass">
							<cover-image class="icon" src="../../static/icon/like.png"></cover-image>
							<cover-view>
								<text class="context">12.5w</text>
							</cover-view>
						</cover-view>
						<cover-view class="iconListClass">
							<cover-image class="icon" src="../../static/icon/like.png"></cover-image>
							<cover-view style="white-space: nowrap;lines:1;">
								<text class="context">12.5w</text>
							</cover-view>
						</cover-view>
					</cover-view>
					<cover-view class="formation">
						<cover-view style="display: flex;align-items: center;flex-direction: row;">
							<cover-image class="header"
								src="https://i02piccdn.sogoucdn.com/a977a5bc3f44ffcf"></cover-image>
							<cover-view>
								<text class="context">作者</text>
							</cover-view>
						</cover-view>
						<cover-view>
							<text class="title">标题标题标题标题标题标题标题标题标题标题标题标题标题标题标题标题标题标题标题标题</text>
						</cover-view>
					</cover-view>
					<cover-view class="duration">
						<cover-view class="long" :style="{width:item.percent+'rpx'}"></cover-view>
					</cover-view>
				</video>
			</swiper-item>

		</swiper>
	</view>
</template>

先来看看在第几个视频时,我们会遇到的情况:

        第1个:可能是视频从尾到头,也可能是从下一个播放器返回。

        第2个:可能是视频第一个过来,也可能是从下一个播放器返回。

        第3个:可能是视频第二个过来,也可能是从下一个播放器返回。

所以在滑动时,我们需要提前先进行判断到底是上滑还是下滑:

qiehuan(e) {

				if (this.oldActiveIndex < e.detail.current) {
					if (this.oldActiveIndex - e.detail.current != -2) {
						console.log('上滑');
						this.index++
						this.next(e.detail.current)
					} else {
						console.log("下滑");
						this.pre(e.detail.current)
						this.index--
					}
				} else if (this.oldActiveIndex > e.detail.current) {
					if (this.oldActiveIndex - e.detail.current != 2) {
						console.log('下滑');
						this.pre(e.detail.current)
						this.index--
					} else {
						console.log("上滑");
						this.index++
						this.next(e.detail.current)
					}
				}
				this.$nextTick(() => {
					this.oldActiveIndex = e.detail.current
				})
			},

我们需要一个变量来记录上一次滑动的播放器下标拿来对比,因为从2到0,和0到2的情况存在,所以判断需要拆开。

然后在对不同情况进行不同的处理,我比较懒,所以代码直接拆开写了。

			next(index) {
				if (Object.is(this.index, 2)) {
				}
				if (Object.is(index, 0)) {
					this.list[1] = this.allList[this.index + 1]
				}
				if (Object.is(index, 1)) {
					this.list[2] = this.allList[this.index + 1]
				}
				if (Object.is(index, 2)) {
					this.list[0] = this.allList[this.index + 1]
				}
				this.play(this.activeIndex)
			},
			pre(index) {
				if (Object.is(this.index, 2)) {
				}
				if (Object.is(index, 0)) {
					this.list[2] = this.allList[this.index - 2]
				}
				if (Object.is(index, 1)) {
					this.list[0] = this.allList[this.index - 2]
				}
				if (Object.is(index, 2)) {
					this.list[1] = this.allList[this.index - 2]
				}
				this.play(this.activeIndex)
			},

主要的处理就是这样,还有一些小功能也做了对应的处理,比如滑动时上一个暂停等等,还有一些小优化,进度条等等。

全部代码:

<template>
	<view style="height: 100vh;flex:1;width:750rpx">
		<swiper @animationfinish="qiehuan" :current="activeIndex" @change="swiper" :circular="index==0?true:false" vertical
			class="list">
			<swiper-item disable-programmatic-animation class="item" v-for="(item,index) in list" :key="index">
				<video @ended="end" @pause='item.active=false' @click="pause(item,index)"
					@timeupdate="long($event,item,index)" :id="'Video'+index" class="video"
					:show-center-play-btn='false' :controls="false"
					src="https://media.w3.org/2010/05/sintel/trailer.mp4">
					<cover-view style="margin-top: 50rpx">
						<text style="color: aqua;">{{item.id}}</text></cover-view>
					<cover-view class="back" v-if="!item.active">
						<cover-image class="pauseIcon" src="../../static/icon/pause.png"></cover-image>
					</cover-view>
					<cover-view class="iconList">
						<cover-view class="iconListClass">
							<cover-image class="icon" src="../../static/icon/like.png"></cover-image>
							<cover-view>
								<text class="context">12.5w</text>
							</cover-view>
						</cover-view>
						<cover-view class="iconListClass">
							<cover-image class="icon" src="../../static/icon/like.png"></cover-image>
							<cover-view>
								<text class="context">12.5w</text>
							</cover-view>
						</cover-view>
						<cover-view class="iconListClass">
							<cover-image class="icon" src="../../static/icon/like.png"></cover-image>
							<cover-view style="white-space: nowrap;lines:1;">
								<text class="context">12.5w</text>
							</cover-view>
						</cover-view>
					</cover-view>
					<cover-view class="formation">
						<cover-view style="display: flex;align-items: center;flex-direction: row;">
							<cover-image class="header"
								src="https://i02piccdn.sogoucdn.com/a977a5bc3f44ffcf"></cover-image>
							<cover-view>
								<text class="context">作者</text>
							</cover-view>
						</cover-view>
						<cover-view>
							<text class="title">标题标题标题标题标题标题标题标题标题标题标题标题标题标题标题标题标题标题标题标题</text>
						</cover-view>
					</cover-view>
					<cover-view class="duration">
						<cover-view class="long" :style="{width:item.percent+'rpx'}"></cover-view>
					</cover-view>
				</video>
			</swiper-item>

		</swiper>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				// 视频下标,记录播放到第几个
				index: 0,
				// 数据
				allList: [{
						src: 'https://media.w3.org/2010/05/sintel/trailer.mp4',
						active: false,
						currentTime: 0,
						duration: 121,
						percent: 0,
						id: 0
					},
					{
						src: 'https://media.w3.org/2010/05/sintel/trailer.mp4',
						active: false,
						currentTime: 0,
						duration: 121,
						percent: 0,
						id: 1
					},
					{
						src: 'https://media.w3.org/2010/05/sintel/trailer.mp4',
						active: false,
						currentTime: 0,
						duration: 121,
						percent: 0,
						id: 2
					},
					{
						src: 'https://media.w3.org/2010/05/sintel/trailer.mp4',
						active: false,
						currentTime: 0,
						duration: 121,
						percent: 0,
						id: 3
					},
					{
						src: 'https://media.w3.org/2010/05/sintel/trailer.mp4',
						active: false,
						currentTime: 0,
						duration: 121,
						percent: 0,
						id: 4
					},
					{
						src: 'https://media.w3.org/2010/05/sintel/trailer.mp4',
						active: false,
						currentTime: 0,
						duration: 121,
						percent: 0,
						id: 5
					},
					{
						src: 'https://media.w3.org/2010/05/sintel/trailer.mp4',
						active: false,
						currentTime: 0,
						duration: 121,
						percent: 0,
						id: 6
					},
					{
						src: 'https://media.w3.org/2010/05/sintel/trailer.mp4',
						active: false,
						currentTime: 0,
						duration: 121,
						percent: 0,
						id: 7
					},
					{
						src: 'https://media.w3.org/2010/05/sintel/trailer.mp4',
						active: false,
						currentTime: 0,
						duration: 121,
						percent: 0,
						id: 8
					},
					{
						src: 'https://media.w3.org/2010/05/sintel/trailer.mp4',
						active: false,
						currentTime: 0,
						duration: 121,
						percent: 0,
						id: 9
					},
					{
						src: 'https://media.w3.org/2010/05/sintel/trailer.mp4',
						active: false,
						currentTime: 0,
						duration: 121,
						percent: 0,
						id: 10
					},
				],
				list: [{
						src: 'https://media.w3.org/2010/05/sintel/trailer.mp4',
						active: false,
						currentTime: 0,
						duration: 121,
						percent: 0,
						id: 0
					},
					{
						src: 'https://media.w3.org/2010/05/sintel/trailer.mp4',
						active: false,
						currentTime: 0,
						duration: 121,
						percent: 0,
						id: 1
					},
					{
						src: 'https://media.w3.org/2010/05/sintel/trailer.mp4',
						active: false,
						currentTime: 0,
						duration: 121,
						percent: 0,
						id: 2
					},
				],
				// 播放器下标
				activeIndex: 0,
				// 记录上一个播放器下标
				oldActiveIndex: 0
			}
		},
		onReady() {},
		methods: {
			qiehuan(e) {
				if (this.oldActiveIndex < e.detail.current) {
					if (this.oldActiveIndex - e.detail.current != -2) {
						console.log('上滑');
						this.index++
						this.next(e.detail.current)
					} else {
						console.log("下滑");
						this.pre(e.detail.current)
						this.index--
					}
				} else if (this.oldActiveIndex > e.detail.current) {
					if (this.oldActiveIndex - e.detail.current != 2) {
						console.log('下滑');
						this.pre(e.detail.current)
						this.index--
					} else {
						console.log("上滑");
						this.index++
						this.next(e.detail.current)
					}
				}
				this.$nextTick(() => {
					this.oldActiveIndex = e.detail.current
				})
			},
			next(index) {
				if (Object.is(this.index, 2)) {
				}
				if (Object.is(index, 0)) {
					this.list[1] = this.allList[this.index + 1]
				}
				if (Object.is(index, 1)) {
					this.list[2] = this.allList[this.index + 1]
				}
				if (Object.is(index, 2)) {
					this.list[0] = this.allList[this.index + 1]
				}
				this.play(this.activeIndex)
			},
			pre(index) {
				if (Object.is(this.index, 2)) {
				}
				if (Object.is(index, 0)) {
					this.list[2] = this.allList[this.index - 2]
				}
				if (Object.is(index, 1)) {
					this.list[0] = this.allList[this.index - 2]
				}
				if (Object.is(index, 2)) {
					this.list[1] = this.allList[this.index - 2]
				}
				this.play(this.activeIndex)
			},
			play(index) {
				for (let i = 0; i < 3; i++) {
					uni.createVideoContext(`Video${i}`, this).pause()
				}
				this.videoContext = uni.createVideoContext('Video' + index, this)
				this.list[index].active = true
				this.videoContext.play()
			},
			swiper(e) {
				// console.log(e.detail.current);
				this.activeIndex = e.detail.current
				// this.play(e.detail.current)
				// this.play(this.activeIndex)
			},
			end(e) {
				this.activeIndex++
				if (Object.is(this.activeIndex, 3)) {
					this.activeIndex = 0
				}
			},
			long(e, item, index) {
				item.currentTime = e.detail.currentTime
				item.percent = (e.detail.currentTime / e.detail.duration) * 750
			},
			pause(item, index) {
				this.videoContext = uni.createVideoContext('Video' + index, this)
				if (item.active) {
					this.videoContext.pause()
				} else {
					this.videoContext.play()
				}
				this.list[index].active = !this.list[index].active
			},
			videoClick() {

			}
		}
	}
</script>

<style>
	.list {
		background-color: black;
		flex: 1;
		width: 750rpx;
	}

	.video {
		flex: 1;
		width: 750rpx;
	}

	.back {
		background-color: rgba(225, 225, 225, 0.3);
		flex: 1;
		width: 750rpx;
		justify-content: center;
		align-items: center;
	}

	.pauseIcon {
		position: absolute;
		left: calc(50% - 25px);
		top: calc(50% - 30px);
		width: 50px;
		height: 60px;
	}

	.duration {
		position: absolute;
		left: 0px;
		bottom: 0px;
		width: 100vw;
		height: 3px;
	}

	.long {
		height: 100%;
		background-color: white;
	}



	.formation {
		flex-direction: column;
		position: absolute;
		left: 10px;
		bottom: 10px;
		width: calc(100% - 80px);
		display: flex;
	}

	.header {
		width: 40px;
		height: 40px;
		border-radius: 50%;
	}

	.iconListClass {
		justify-content: center;
		align-items: center;
	}

	.context {
		white-space: nowrap;
		width: 100rpx;
		color: white;
		margin-left: 5px;
		font-size: 13px;
		font-weight: bold;
	}

	.title {
		width: 600rpx;
		margin-top: 10px;
		color: white;
		margin-left: 5px;
		font-size: 13px;
		font-weight: bold;
		text-overflow: ellipsis;
		overflow: hidden;
		white-space: nowrap;
		lines: 1;
	}


	.iconList {
		display: flex;
		flex-direction: column;
		justify-content: center;
		align-items: center;
		width: 40px;
		position: absolute;
		right: 10px;
		bottom: 10px;
	}

	.icon {
		margin-top: 10px;
		width: 40px;
		height: 40px;
	}

	.context {
		text-align: center;
		color: white;
		font-size: 13px;
	}
</style>

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值