uniapp中swiper的大坑,在swiper-item中嵌套video在移动端无法实现上下滑动的,要用nvue代替,从而实现抖音滑视频效果

问题描述:

想做一个抖音滑屏切换视频的效果,结果。。。。。

研究了一天,发现在swiper-item中嵌套视频时,移动端只能滑动切换背景,视频在原位置是不会动的。。。。但是在h5端和小程序端可以完美运行,这就很让人生气了:

 在移动端的时候,就会出现虽然切换到第二个视频了,声音也出来了,但是画面就是没有动。。。。。还是上一个视频的画面,这是因为视频的层级较高导致的,需要使用nvue方案:

介绍 | uni-app官网:使用nvue的教程;

这句话就很重要了,因为h5页面端切换是咩有问题的,所以需要为移动端单独建一个nvue文件,这时候就用到了这条规则: 

为移动端创建单独的页面:包括视频组件也是要单独创建一个nvue的

videos.vue代码:

<template>
	<swiper class="swiper" :vertical="true" @change="change" @touchstart="touchStart" @touchend="touchEnd">
		<swiper-item v-for="item of list" :key="item.id" class="swiper-item">
			<videoPlayer :video="item" ref="player"></videoPlayer>
			<!-- <image :src="item.preImg" mode="" class="swiper-img"></image> -->
		</swiper-item>
	</swiper>
</template>

<script>
	import videoPlayer from "./components/videoPlayer.vue"
	var time = null
	export default {
		props: ["myList"],
		components: {
			videoPlayer,
		},
		name: "video-list",
		data() {
			return {
				list: [{
						id: 1,
						preImg: "http://p1-q.mafengwo.net/s16/M00/8D/4D/CoUBUmFZOWKAA8mQAA8Oww0vs7k240.jpg",
						src: "https://txmov2.a.yximgs.com/upic/2020/11/08/19/BMjAyMDExMDgxOTQxNTlfNTIzNDczMzQ0XzM4OTQ1MDk5MTI4XzFfMw==_b_Bc770a92f0cf153407d60a2eddffeae2a.mp4"
					},
					{
						id: 2,
						preImg: "http://b1-q.mafengwo.net/s16/M00/23/F3/CoUBUmFbN5OAGqEkAA4O0V-U1uo031.jpg",
						src: "https://txmov2.a.yximgs.com/upic/2020/10/02/09/BMjAyMDEwMDIwOTAwMDlfMTIyMjc0NTk0Ml8zNjk3Mjg0NjcxOF8xXzM=_b_B28a4518e86e2cf6155a6c1fc9cf79c6d.mp4"
					},
					{
						id: 3,
						preImg: "http://p1-q.mafengwo.net/s16/M00/23/F4/CoUBUmFbN5WAbMikAA5cYlWno5U709.jpg",
						src: "https://txmov6.a.yximgs.com/upic/2020/08/23/00/BMjAyMDA4MjMwMDMyNDRfMTYzMzY5MDA0XzM0ODI4MDcyMzQ5XzFfMw==_b_B9a1c9d4e3a090bb2815994d7f33a906a.mp4"
					},
					{
						id: 4,
						preImg: "http://p1-q.mafengwo.net/s18/M00/E6/C0/CoUBYGFceTyAOSBqABFXcMPFJ1w112.jpg",
						src: "https://alimov2.a.yximgs.com/upic/2020/07/02/14/BMjAyMDA3MDIxNDUyMDlfOTExMjIyMjRfMzE1OTEwNjAxNTRfMV8z_b_Bf3005d42ce9c01c0687147428c28d7e6.mp4"
					}
				],
				pageStartY: 0,
				pageEndY: 0,
				swiperHeight: "0"
			};
		},
		methods: {
			change(res) {
				clearTimeout(time)
				this.page = res.detail.current
				time = setTimeout(() => {
					if (this.pageStartY > this.pageEndY) {
						console.log("向上滑动" + this.page);
						this.$refs.player[this.page].playVideo()
						this.$refs.player[this.page - 1].pauseVideo()
						this.pageStartY = 0
						this.pageEndY = 0
					} else {
						console.log("向下滑动" + this.page);
						this.$refs.player[this.page].playVideo()
						this.$refs.player[this.page + 1].pauseVideo()
						this.pageStartY = 0
						this.pageEndY = 0
					}
				}, 1)
			},
			touchStart(res) {
				this.pageStartY = res.changedTouches[0].pageY;
				console.log(this.pageStartY);
			},
			touchEnd(res) {
				this.pageEndY = res.changedTouches[0].pageY;
				console.log(this.pageEndY);
			}
		},
		watch: {
			myList() {
				this.list = this.myList;
			}
		}
	}
</script>

<style>
	.swiper {
		width: 100vw;
		height: 100vh;
	}

	.swiper-item {
		width: 100vw;
		height: 100vh;
		z-index: 9;
	}

	.swiper-img {
		width: 100%;
		height: 100%;
	}

	/* 	.left-box {
		z-index: 20;
		position: absolute;
		bottom: 50px;
		left: 10px;
	}

	.right-box {
		z-index: 20;
		position: absolute;
		bottom: 50px;
		right: 10px;
	} */
</style>

videoPlayer.vue代码:

<template>
	<video :id="'myVideo'+ video.id" @click="click" @play="changePlay" class="video" :controls="false" :loop="false"
		:src="video.src">
	</video>
</template>

<script>
	export default {
		props: ['video'],
		name: "videoPlayer",
		data() {
			return {
				play: false
			};
		},
		onReady() {
			this.videoContext = uni.createVideoContext("myVideo" + this.video.id, this)
			console.log("视频组件onready:");
			// console.log(this.videoContext);
		},
		mounted() {
			this.videoContext = uni.createVideoContext("myVideo" + this.video.id, this)
		},
		methods: {
			click() {
				console.log("click", this.video.id, this.play);
				if (!this.play) {
					this.playthis()
				} else {
					this.pauseVideo()
				}
			},
			playVideo() {
				if (this.play === false) {
					console.log("playVideo", this.video.id);
					this.videoContext.seek(0)
					this.videoContext.play()
					this.play = true
				}
			},
			pauseVideo() {
				console.log("pauseVideo", this.video.id);
				if (this.play === true) {
					this.videoContext.pause()
					this.play = false
				}
			},
			playthis() {
				// console.log("playthis", this.video.id);
				if (this.play === false) {
					console.log("playthis", this.video.id);
					this.videoContext.play()
					this.play = true
				}
			},
			changePlay() {
				console.log("changePlay");
				this.play = true
			}
		}
	}
</script>

<style>
	.video {
		width: 100vw;
		height: 100vh;
		/* z-index: 1; */
	}

	/* 	.video {
		height: 100%;
		width: 100%;
		z-index: 1;
	} */
</style>

videos.nvue代码:

<template>
	<swiper class="swiper" :style="{height:screenHeight+'px'}" :vertical="true" @change="change"
		@touchstart="touchStart" @touchend="touchEnd">
		<swiper-item v-for="item of list" :style="{height:screenHeight+'px'}" :key="item.id" class="swiper-item">
			<videoPlayer :video="item" :screenHeight="screenHeight" ref="player"></videoPlayer>
		</swiper-item>
	</swiper>
</template>

<script>
	import videoPlayer from "./components/videoPlayer.nvue"
	var time = null
	export default {
		props: ["myList"],
		components: {
			videoPlayer,
		},
		name: "video-list",
		data() {
			return {
				list: [{
						id: 1,
						preImg: "http://p1-q.mafengwo.net/s16/M00/8D/4D/CoUBUmFZOWKAA8mQAA8Oww0vs7k240.jpg",
						src: "https://txmov2.a.yximgs.com/upic/2020/11/08/19/BMjAyMDExMDgxOTQxNTlfNTIzNDczMzQ0XzM4OTQ1MDk5MTI4XzFfMw==_b_Bc770a92f0cf153407d60a2eddffeae2a.mp4"
					},
					{
						id: 2,
						preImg: "http://b1-q.mafengwo.net/s16/M00/23/F3/CoUBUmFbN5OAGqEkAA4O0V-U1uo031.jpg",
						src: "https://txmov2.a.yximgs.com/upic/2020/10/02/09/BMjAyMDEwMDIwOTAwMDlfMTIyMjc0NTk0Ml8zNjk3Mjg0NjcxOF8xXzM=_b_B28a4518e86e2cf6155a6c1fc9cf79c6d.mp4"
					},
					{
						id: 3,
						preImg: "http://p1-q.mafengwo.net/s16/M00/23/F4/CoUBUmFbN5WAbMikAA5cYlWno5U709.jpg",
						src: "https://txmov6.a.yximgs.com/upic/2020/08/23/00/BMjAyMDA4MjMwMDMyNDRfMTYzMzY5MDA0XzM0ODI4MDcyMzQ5XzFfMw==_b_B9a1c9d4e3a090bb2815994d7f33a906a.mp4"
					},
					{
						id: 4,
						preImg: "http://p1-q.mafengwo.net/s18/M00/E6/C0/CoUBYGFceTyAOSBqABFXcMPFJ1w112.jpg",
						src: "https://alimov2.a.yximgs.com/upic/2020/07/02/14/BMjAyMDA3MDIxNDUyMDlfOTExMjIyMjRfMzE1OTEwNjAxNTRfMV8z_b_Bf3005d42ce9c01c0687147428c28d7e6.mp4"
					}
				],
				pageStartY: 0,
				pageEndY: 0,
				screenHeight: 0
			};
		},
		mounted() {
			uni.getSystemInfo({
				success: (res) => {
					console.log("首页获取到的页面高度:windowHeight", res.windowHeight);
					this.screenHeight = res.windowHeight
				}
			});
		},
		methods: {
			change(res) {
				clearTimeout(time)
				this.page = res.detail.current
				time = setTimeout(() => {
					if (this.pageStartY > this.pageEndY) {
						console.log("向上滑动" + this.page);
						this.$refs.player[this.page].playVideo()
						this.$refs.player[this.page - 1].pauseVideo()
						this.pageStartY = 0
						this.pageEndY = 0
					} else {
						console.log("向下滑动" + this.page);
						this.$refs.player[this.page].playVideo()
						this.$refs.player[this.page + 1].pauseVideo()
						this.pageStartY = 0
						this.pageEndY = 0
					}
				}, 1)
			},
			touchStart(res) {
				this.pageStartY = res.changedTouches[0].pageY;
				console.log(this.pageStartY);
			},
			touchEnd(res) {
				this.pageEndY = res.changedTouches[0].pageY;
				console.log(this.pageEndY);
			}
		},
		watch: {
			myList() {
				this.list = this.myList;
			}
		}
	}
</script>

<style>
	.swiper {
		width: 750rpx;
		/* height: 500vh; */
	}

	.swiper-item {
		width: 750rpx;
		/* height: 500vh; */
		z-index: 9;
	}

	.swiper-img {
		width: 100vw;
		height: 100vh;
	}

	/* 	.left-box {
		z-index: 20;
		position: absolute;
		bottom: 50px;
		left: 10px;
	}

	.right-box {
		z-index: 20;
		position: absolute;
		bottom: 50px;
		right: 10px;
	} */
</style>

videoPlayer.nvue代码:

<template>
	<video :id="'myVideo'+ video.id" @click="click" @play="changePlay" class="video" :controls="false" :loop="false"
		:src="video.src" :poster="video.preImg" :style="{height:screenHeight+'px'}">
	</video>
</template>

<script>
	export default {
		props: ['video', "screenHeight"],
		name: "videoPlayer",
		data() {
			return {
				play: false,
				screenHeight: 0
			};
		},
		onReady() {
			this.videoContext = uni.createVideoContext("myVideo" + this.video.id, this)
			console.log("视频组件onready:");
		},
		mounted() {
			this.videoContext = uni.createVideoContext("myVideo" + this.video.id, this)
		},
		methods: {
			click() {
				console.log("click", this.video.id, this.play);
				if (!this.play) {
					this.playthis()
				} else {
					this.pauseVideo()
				}
			},
			playVideo() {
				if (this.play === false) {
					console.log("playVideo", this.video.id);
					this.videoContext.seek(0)
					this.videoContext.play()
					this.play = true
				}
			},
			pauseVideo() {
				console.log("pauseVideo", this.video.id);
				if (this.play === true) {
					this.videoContext.pause()
					this.play = false
				}
			},
			playthis() {
				// console.log("playthis", this.video.id);
				if (this.play === false) {
					console.log("playthis", this.video.id);
					this.videoContext.play()
					this.play = true
				}
			},
			changePlay() {
				console.log("changePlay");
				this.play = true
			}
		}
	}
</script>

<style>
	.video {
		width: 750rpx;
	}

	/* 	.video {
		height: 100%;
		width: 100%;
		z-index: 1;
	} */
</style>

唯一不一样的就是增加了动态适配手机屏幕高度的方法:注意获取到的高度单位是px,不要用混了,我之前用的视口单位,结果。。。。惨剧

为什么要动态设置这个高度呢?因为官方说了:

nvue所支持的通用样式已在本文档中全部列出,一些组件可能有自定义样式,请参考组件文档。除此之外的属性,均不被支持。 | uni-app官网

 我也很无语啊。。。。。。。。。

那就只能这么写了,为什么不动态设置宽度呢?因为官方默认把屏幕划分为750等份了,所以宽的设置:

 同样子组件中也要设置高度:

最终的效果就是移动端和h5还有小程序都可以完美上下滑动了

猜你喜欢

转载自blog.csdn.net/weixin_44786530/article/details/125984113