Problem Description:
I want to make a Douyin sliding screen switching video effect, the result. . . . .
After researching for a day, I found that when a video is embedded in a swiper-item, the mobile terminal can only slide to switch the background, and the video will not move in its original position. . . . But it can run perfectly on the h5 side and the applet side, which is very annoying:
On the mobile terminal, it will appear that although the video is switched to the second video and the sound comes out, the picture does not move. . . . . It’s still the same picture as the previous video. This is because the video has a higher level, so you need to use the nvue solution:
Introduction | uni-app official website : tutorial on using nvue;
This sentence is very important, because switching between h5 pages is problematic, so a separate nvue file needs to be created for the mobile terminal. This rule is used at this time:
Create a separate page for the mobile terminal: including the video component, you also need to create a separate nvue
videos.vue code:
<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 code:
<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 code:
<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 code:
<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>
The only difference is the addition of a method to dynamically adapt to the height of the mobile phone screen: note that the obtained height unit is px, do not mix it with the viewport unit I used before, and the result is. . . . Tragedy
Why do we need to set this height dynamically? Because the official said:
I am also speechless. . . . . . . . .
That’s all we can do, why not set the width dynamically? Because the official default divides the screen into 750 equal parts, the width setting is:
Similarly, the height must be set in the subcomponent:
The final effect is that the mobile terminal, h5 and small programs can slide up and down perfectly.