实机效果
首先,代码最主要的思路先说明一下:
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>