uniapp 小程序视频滑动 ,两个页面共同使用同一套视频

由于是两个页面同时使用数据,使用了组合式 和 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>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值