由于是两个页面同时使用数据,使用了组合式 和 pinia 共通处理。
// UseVideo.ts
import { videoreceivepoints } from '@/api/home'
import { useVideoStore } from '@/stores/counter'
export function UseVideo() {
const VideoStore = useVideoStore();
const receNum = ref(0);
const receshow = ref(false);
//点击播放&&暂停 只是修改状态
const tapVideoHover = (state: string) => {
if (state == 'play' || state == 'continue') {
VideoStore.videoList[VideoStore.VideocurrentIndex].state = 'pause'
} else {
VideoStore.videoList[VideoStore.VideocurrentIndex].state = 'continue'
}
if (VideoStore.videoList[VideoStore.VideocurrentIndex].state == 'continue') {
playVideo()
}
if (VideoStore.videoList[VideoStore.VideocurrentIndex].state == 'pause') {
pauseVideo()
}
}
const animationfinish = () => {
if (VideoStore.VideocurrentIndex == VideoStore.videoList.length - 1) {
VideoStore.increment()
}
}
//播放实时进度
const videoTimeUpdateEvent = async (e: any, ind: number) => {
let remainingSeconds = Math.floor(Number(e.detail.duration) - Number(e.detail.currentTime))
VideoStore.videoList[ind].remainingMinutes = Math.floor(remainingSeconds / 60)
.toString()
.padStart(2, '0')
VideoStore.videoList[ind].remainingSecondsInMinute = Math.floor(remainingSeconds % 60)
.toString()
.padStart(2, '0')
VideoStore.videoCurrentTime = e.detail.currentTime
VideoStore.videoList[ind].DrawDown = remainingSeconds;
}
//播放完成后处理
const getPoints = async (id: number) => {
console.log('播放完成')
}
//开始播放
const playVideo = (ind?: number) => {
if (ind !== undefined && VideoStore.VideocurrentIndex !== ind) {
VideoStore.VideocurrentIndex = ind
} else {
uni.createVideoContext(returnID()).seek(VideoStore.videoCurrentTime)
}
nextTick(() => {
uni.createVideoContext(returnID()).play()
VideoStore.videoList[VideoStore.VideocurrentIndex].state = 'play'
VideoStore.videoList[VideoStore.VideocurrentIndex].playIng = true
})
}
//暂停播放
const pauseVideo = (ind?: number) => {
if (ind !== undefined) {
uni.createVideoContext(returnID(ind)).pause();
VideoStore.videoList[ind].state = 'pause'
VideoStore.videoList[ind].playIng = false
} else {
uni.createVideoContext(returnID()).pause();
VideoStore.videoList[VideoStore.VideocurrentIndex].state = 'pause'
VideoStore.videoList[VideoStore.VideocurrentIndex].playIng = false
}
}
const returnID = (ind?: number) => {
if (ind !== undefined) {
return `video_${VideoStore.videoList[ind].video_id}`
} else {
return `video_${VideoStore.videoList[VideoStore.VideocurrentIndex].video_id}`
}
}
const changeVideo = (event: any) => {
VideoStore.VideocurrentIndex = event.detail.current
}
return {
VideoStore,
videoTimeUpdateEvent,
playVideo,
pauseVideo,
getPoints,
tapVideoHover,
animationfinish,
changeVideo,
receNum,
receshow
}
}
//counter.ts
import { defineStore } from 'pinia'
import { videoIndex } from '@api/home'
import type { Video } from "@api/home.d"
export const useVideoStore = defineStore('video', {
state: () => {
return {
videoList: [] as Video[],
VideocurrentIndex: 0,
videoCurrentPage: 1,
videoCurrentTime: 0, // 当前进度,作为两个页面共同查看同一个视频时的进度,也可以把进度做到列表中
videoFlag: false
}
},
actions: {
increment() {
if (!this.videoFlag) {
return new Promise(async (resolve, reject) => {
const { code, data } = await videoIndex({
page: this.videoCurrentPage,
list_rows: 5,
})
if (code == 1) {
this.videoCurrentPage++
data.data.data.forEach(item => {
item.state = 'play';
item.playIng = false
})
this.videoList.push(...data.data.data)
this.videoFlag = (data.data.total === this.videoList.length)
resolve(true)
} else {
reject();
}
})
}else{
return Promise.resolve(false)
}
},
},
})
两个vue文件,可交互
//视频列表
<template>
<view class="pageBg">
<view class="videoListBox">
<view
v-for="(item, index) in videoEd.VideoStore.videoList"
:key="index"
:class="['videoItem', { Videocurrent: videoEd.VideoStore.VideocurrentIndex == index }]">
<video
:id="`video_${item.video_id}`"
class="videoValue"
:src="item.video_url"
@error="videoErrorCallback"
enable-danmu
:direction="0"
:show-fullscreen-btn="false"
:enable-progress-gesture="false"
:vslide-gesture-in-fullscreen="false"
:controls="false"
:show-center-play-btn="false"
loop
@click="seeDetail(index)"
@ended="videoEd.getPoints(item.video_id)"
@timeupdate="videoEd.videoTimeUpdateEvent($event, index)">
<view
class="Videocover"
v-if="videoEd.VideoStore.VideocurrentIndex !== index"
@click.prevent="videoEd.playVideo(index)">
<image :src="item.video_logo" class="img" />
<image src="@img/homeEntry/play.png" class="playIcon"></image>
</view>
<view class="videoContent">
<view class="tips f_s_c">
<image src="@img/homeEntry/pointsIcon.png" class="poinsIcon"></image>
<view class="fs24 white difference">{{ item.DrawDown }}秒后可得奖励</view>
</view>
<view class="videoTime" v-if="item.remainingSecondsInMinute">
{{ item.remainingMinutes }}:{{ item.remainingSecondsInMinute }}
</view>
</view>
</video>
<view class="f_bet_c videoInfo">
<view class="ellipsis fs30 flex1">{{ item.video_name }}</view>
<button open-type="share" class="shareBtn">
<image src="@img/homeEntry/share.png" class="m_l_35 shareIcon"></image>
</button>
</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { navigateToPage, showToast } from '@/utils/init'
import { UseVideo } from './UseVideo'
import received from "@components/received.vue"
const videoEd = UseVideo()
let onceLoad = true; //记录是不是第一次
let inddsa: number; // 记录旧视频,如果不是第一次播放静止。 出现过疑难杂症才加的。
onLoad(() => {
videoEd.VideoStore.increment().then(() => {
videoEd.playVideo()
onceLoad = false
})
})
onShow(() => {
if (!onceLoad) {
videoEd.pauseVideo(inddsa)
videoEd.playVideo()
}
})
onHide(() => {
videoEd.pauseVideo()
videoEd.pauseVideo(inddsa)
})
const seeDetail = (ind: number) => {
if (videoEd.VideoStore.VideocurrentIndex == ind) {
inddsa = ind
navigateToPage(`/pages/index/videoInfo?option=${ind}`)
}
}
const videoErrorCallback = (err: any) => {
console.log(err)
showToast('视频加载错误')
}
onReachBottom(() => {
videoEd.VideoStore.increment()
})
</script>
<style lang="scss" scoped>
.videoListBox {
padding: 0 38rpx;
.videoItem {
background: #ffffff;
box-shadow: 0rpx 2rpx 10rpx 0rpx rgba(201, 201, 201, 0.75);
border-radius: 10rpx;
overflow: hidden;
margin-top: 30rpx;
&.Videocurrent {
position: sticky;
top: 0;
// bottom: 0;
z-index: 999;
}
&:nth-child(1) {
margin-top: 0;
}
.videoValue {
width: 100%;
height: 316rpx;
position: relative;
.Videocover {
.img {
width: 100%;
height: 316rpx;
}
.playIcon {
width: 54rpx;
height: 54rpx;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
.videoContent {
padding: 15rpx;
.difference {
mix-blend-mode: lighten;
}
.tips {
.poinsIcon {
width: 40rpx;
height: 40rpx;
}
}
.videoTime {
position: absolute;
right: 30rpx;
bottom: 20rpx;
width: 86rpx;
height: 30rpx;
background: rgba($color: #000000, $alpha: 0.5);
border-radius: 15rpx;
text-align: center;
line-height: 30rpx;
font-size: 22rpx;
font-weight: 400;
color: #fefefe;
}
}
}
.videoInfo {
padding: 20rpx;
.shareBtn {
background: transparent;
border: none;
flex-shrink: 0;
padding: 0;
&:after {
border: none;
}
.shareIcon {
width: 36rpx;
height: 36rpx;
}
}
}
}
}
</style>
// 全屏视频, a.vue
<template>
<view class="topBack" :style="{ 'padding-top': `${TopHeight?.statusBarHeight}` }">
<uni-icons type="back" size="30" color="#ffffff" @click="backPage"></uni-icons>
</view>
<swiper
class="pageAll"
:vertical="true"
@animationfinish="videoEd.animationfinish"
@change="videoEd.changeVideo"
:current="VideoStore.VideocurrentIndex"
:indicator-dots="false"
:duration="300"
@touchstart="mpTouchstart"
@touchend="mpTouchend">
<swiper-item v-for="(item, index) in VideoStore.videoList" :key="index">
<view v-if="Math.abs(VideoStore.VideocurrentIndex - index) <= 1" class="VideoItem">
<view>
<video
:id="`video_${item.video_id}`"
:loop="true"
:controls="false"
:page-gesture="false"
:show-fullscreen-btn="false"
:show-loading="false"
:show-center-play-btn="false"
:enable-progress-gesture="false"
:src="item.video_url"
@click="videoEd.tapVideoHover(item.state)"
class="pageAll"
@ended="videoEd.getPoints(item.video_id)"
@timeupdate="videoEd.videoTimeUpdateEvent($event, index)"
:poster="item.video_logo"></video>
<image
v-if="!item.playIng"
:src="item.video_logo"
class="pageAll poa"
mode="aspectFit"></image>
</view>
<view class="tips f_s_c">
<image src="@img/homeEntry/pointsIcon.png" class="poinsIcon"></image>
<view class="fs24 white difference">{{ item.DrawDown }}秒后可得奖励</view>
</view>
<view
class="videoHover pageAll"
@click="videoEd.tapVideoHover(item.state)"
v-if="item.state !== 'continue' && item.state !== 'play'">
<image class="playState" src="@img/homeEntry/play.png"></image>
</view>
<button open-type="share" class="shareIcon">
<image src="@img/homeEntry/radioShare.png"></image>
</button>
<view class="BottomTitle">
<text class="white fs28 twoLine">
{{ item.video_name }}
</text>
</view>
</view>
</swiper-item>
</swiper>
</template>
<script setup lang="ts">
import { useVideoStore } from '@stores/counter'
import { backPage } from '@/utils/init'
import { UseVideo } from './UseVideo'
let TopHeight = reactive({
statusBarHeight:0,
titleBarHeight :0,
})
const GetStatusBarHeight = () => {
// #ifndef H5 || APP-PLUS || MP-ALIPAY
let v = uni.getMenuButtonBoundingClientRect()
TopHeight.statusBarHeight = v.top + 'px',
TopHeight.titleBarHeight = v.height + 'px'
// #endif
}
GetStatusBarHeight();
const VideoStore = useVideoStore()
const videoEd = UseVideo()
let mptime: number, mpstartTime: number, changeTimeout: any
onLoad(() => {})
watch(
() => VideoStore.VideocurrentIndex,
async (k, old_k) => {
videoEd.pauseVideo(old_k)
VideoStore.videoList[k].state = 'play'
clearTimeout(changeTimeout)
setTimeout(() => {
VideoStore.videoList[k].playIng = true
}, 250)
if (mptime < 0.2) {
changeTimeout = setTimeout(() => {
videoEd.playVideo()
}, 1400)
} else {
videoEd.playVideo()
}
}
)
const mpTouchstart = () => {
mpstartTime = Number(new Date()) / 1000
}
const mpTouchend = () => {
mptime = Number(new Date()) / 1000 - mpstartTime
}
onShow(() => {
videoEd.playVideo()
})
onHide(() => {
videoEd.pauseVideo()
})
</script>
<style scoped lang="scss">
.topBack {
position: fixed;
// top: 0;
left: 40rpx;
z-index: 999;
}
.pageAll {
width: 100vw;
height: 100vh;
}
.VideoItem {
position: relative;
.tips {
position: absolute;
z-index: 10;
top: 15vh;
left: 30rpx;
.poinsIcon {
width: 40rpx;
height: 40rpx;
}
}
.videoHover {
position: absolute;
top: 0;
left: 0;
flex: 1;
background-color: rgba(0, 0, 0, 0.1);
justify-content: center;
align-items: center;
.playState {
position: absolute;
width: 160rpx;
height: 160rpx;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
opacity: 0.2;
}
}
.shareIcon {
position: absolute;
right: 30rpx;
bottom: 200rpx;
background-color: transparent;
&:after {
border: none;
}
image {
width: 91rpx;
height: 91rpx;
}
}
}
.BottomTitle {
width: 80vw;
z-index: 99;
position: absolute;
bottom: 30px;
padding: 15rpx;
flex-direction: column;
justify-content: flex-start;
color: #ffffff;
}
</style>