<template>
<view class="video-container">
<!-- 垂直滑动容器 -->
<swiper
class="swiper"
:vertical="true"
:duration="300"
:style="{ height: screenHeight + 'px' }"
@change="onSwiperChange"
>
<swiper-item
v-for="(item, index) in videoList"
:key="index"
:style="{ height: screenHeight + 'px' }"
>
<!-- 视频容器 -->
<view class="video-wrapper">
<!-- 只渲染当前和下一个视频 -->
<video
v-if="index === currentIndex || index === nextIndex"
:id="'video-' + index"
class="playVideo"
:src="item.src"
:style="{ height: screenHeight + 'px' }"
object-fit="cover"
:autoplay="index === currentIndex"
:controls="false"
loop
@timeupdate="onTimeUpdate($event, index)"
@loadeddata="onVideoLoadedData(index)"
@play="onVideoPlay(index)"
></video>
<!-- 非当前视频显示封面 -->
<image
v-else
class="cover-image"
:src="item.fmsrc"
mode="aspectFill"
:style="{ height: screenHeight + 'px' }"
></image>
</view>
<!-- 视频信息 -->
<cover-view class="video-info">
<cover-text class="nickname">@Picnic熊</cover-text>
<cover-text class="title">{{ item.title }}</cover-text>
</cover-view>
</swiper-item>
</swiper>
<!-- 播放控制区域 -->
<cover-view class="control-container" :style="{ height: screenHeight + 'px' }">
<!-- 播放/暂停按钮 -->
<cover-image
class="play-icon"
:src="playFlag ? '/static/pause.png' : '/static/play.png'"
@click="togglePlay"
></cover-image>
<!-- 进度条 -->
<cover-view class="progress-container">
<slider
class="slider"
:value="currentProgress"
min="0"
max="100"
activeColor="#6d6d6d"
backgroundColor="#2f2f2f"
block-color="#6d6d6d"
block-size="12"
@change="onSliderChange"
@changing="onSliderChanging"
/>
</cover-view>
<!-- 时间显示 -->
<cover-view class="time-display">
<cover-text>{{ currentTimeDisplay }}</cover-text>
<cover-text>/</cover-text>
<cover-text>{{ totalDurationDisplay }}</cover-text>
</cover-view>
<!-- 滑动时间预览 -->
<cover-view v-if="showTimePreview" class="time-preview">
<cover-text>{{ previewTimeDisplay }}</cover-text>
</cover-view>
</cover-view>
</view>
</template>
<script>
export default {
data() {
return {
screenHeight: 0, // 屏幕高度
currentIndex: 0, // 当前播放的视频索引
nextIndex: 1, // 下一个视频索引(预加载)
videoList: [ // 视频数据
{
"src": "https://minivideo.xiu123.cn/original/79649956972748019fb7761c6852c5b1/25d3d19-17b2a2529da.mp4",
"title": "……… 时过尽迁!",
"fmsrc": "/static/fm1.png",
"duration": 0, // 视频总时长(秒)
"currentTime": 0, // 当前播放时间(秒)
}, {
"src": "https://minivideo.xiu123.cn/original/79649956972748019fb7761c6852c5b1/25d3d19-17b2a2529da.mp4",
"title": "……… 时过尽迁!",
"fmsrc": "/static/fm1.png",
"duration": 0, // 视频总时长(秒)
"currentTime": 0, // 当前播放时间(秒)
},
// 其他视频数据...
],
playFlag: true, // 播放状态
currentProgress: 0, // 进度条百分比
currentTimeDisplay: "00:00", // 当前时间显示
totalDurationDisplay: "00:00", // 总时长显示
previewTimeDisplay: "00:00", // 预览时间显示
showTimePreview: false, // 是否显示预览时间
videoContexts: {}, // 视频上下文对象缓存
isSwiping: false, // 是否正在滑动
};
},
onLoad() {
// 获取屏幕高度
uni.getSystemInfo({
success: (res) => {
this.screenHeight = res.windowHeight;
}
});
},
onReady() {
// 初始化第一个视频
this.initVideoContext(0);
},
methods: {
// 初始化视频上下文
initVideoContext(index) {
if (!this.videoContexts[index]) {
this.videoContexts[index] = uni.createVideoContext(`video-${index}`, this);
}
},
// 滑动切换事件
onSwiperChange(event) {
const newIndex = event.detail.current;
// 暂停当前视频
if (this.videoContexts[this.currentIndex]) {
this.videoContexts[this.currentIndex].pause();
}
// 更新索引
this.currentIndex = newIndex;
this.nextIndex = (newIndex + 1) % this.videoList.length;
// 播放新视频
this.$nextTick(() => {
this.initVideoContext(newIndex);
if (this.videoContexts[newIndex]) {
this.videoContexts[newIndex].play();
}
// 重置进度条
this.currentProgress = 0;
this.currentTimeDisplay = "00:00";
this.playFlag = true;
// 预加载下一个视频
this.initVideoContext(this.nextIndex);
});
// 滑动到底部加载更多
if (newIndex >= this.videoList.length - 2) {
this.loadMoreVideos();
}
},
// 加载更多视频
async loadMoreVideos() {
try {
// 模拟API请求获取新视频
const newVideos = [
{
"src": "https://minivideo.xiu123.cn/original/new-video1.mp4",
"title": "新视频1",
"fmsrc": "/static/fm-new1.png",
"duration": 0,
"currentTime": 0
},
{
"src": "https://minivideo.xiu123.cn/original/new-video2.mp4",
"title": "新视频2",
"fmsrc": "/static/fm-new2.png",
"duration": 0,
"currentTime": 0
}
];
// 添加新视频
this.videoList = [...this.videoList, ...newVideos];
} catch (error) {
console.error("加载更多视频失败:", error);
}
},
// 视频加载完成
onVideoLoadedData(index) {
// 获取视频时长
if (this.videoContexts[index]) {
this.videoContexts[index].duration = (duration) => {
this.videoList[index].duration = duration;
this.totalDurationDisplay = this.formatTime(duration);
};
}
},
// 视频播放事件
onVideoPlay(index) {
if (index === this.currentIndex) {
this.playFlag = true;
}
},
// 时间更新事件
onTimeUpdate(event, index) {
if (index !== this.currentIndex) return;
const currentTime = event.detail.currentTime;
const duration = this.videoList[index].duration || 1; // 避免除以0
// 更新当前时间
this.videoList[index].currentTime = currentTime;
this.currentTimeDisplay = this.formatTime(currentTime);
// 更新进度条
if (!this.isSwiping) {
this.currentProgress = (currentTime / duration) * 100;
}
},
// 切换播放状态
togglePlay() {
this.playFlag = !this.playFlag;
if (this.videoContexts[this.currentIndex]) {
if (this.playFlag) {
this.videoContexts[this.currentIndex].play();
} else {
this.videoContexts[this.currentIndex].pause();
}
}
},
// 进度条变化结束
onSliderChange(event) {
const value = event.detail.value;
const currentVideo = this.videoList[this.currentIndex];
const duration = currentVideo.duration || 1;
// 计算跳转时间
const seekTime = (value / 100) * duration;
// 跳转到指定时间
if (this.videoContexts[this.currentIndex]) {
this.videoContexts[this.currentIndex].seek(seekTime);
}
// 重置状态
this.isSwiping = false;
this.showTimePreview = false;
},
// 进度条变化中
onSliderChanging(event) {
const value = event.detail.value;
const currentVideo = this.videoList[this.currentIndex];
const duration = currentVideo.duration || 1;
// 计算预览时间
const previewTime = (value / 100) * duration;
this.previewTimeDisplay = this.formatTime(previewTime);
// 显示预览
this.isSwiping = true;
this.showTimePreview = true;
},
// 格式化时间 (秒 -> mm:ss)
formatTime(seconds) {
const min = Math.floor(seconds / 60);
const sec = Math.floor(seconds % 60);
return `${min.toString().padStart(2, '0')}:${sec.toString().padStart(2, '0')}`;
},
// 页面显示时恢复播放
onShow() {
if (this.videoContexts[this.currentIndex]) {
this.videoContexts[this.currentIndex].play();
this.playFlag = true;
}
},
// 页面隐藏时暂停播放
onHide() {
if (this.videoContexts[this.currentIndex]) {
this.videoContexts[this.currentIndex].pause();
this.playFlag = false;
}
}
}
};
</script>
<style lang="scss" scoped>
.video-container {
width: 100%;
height: 100vh;
position: relative;
background-color: #000;
}
.swiper {
width: 100%;
}
.video-wrapper {
position: relative;
width: 100%;
height: 100%;
.playVideo, .cover-image {
width: 100%;
height: 100%;
display: block;
background-color: #000;
}
.cover-image {
object-fit: cover;
}
}
.video-info {
position: absolute;
bottom: 120rpx;
left: 30rpx;
z-index: 10;
.nickname {
color: #fff;
font-size: 36rpx;
font-weight: bold;
display: block;
margin-bottom: 15rpx;
text-shadow: 0 1rpx 3rpx rgba(0, 0, 0, 0.8);
}
.title {
color: #fff;
font-size: 28rpx;
width: 500rpx;
display: block;
text-overflow: ellipsis;
white-space: normal;
line-height: 1.4;
text-shadow: 0 1rpx 3rpx rgba(0, 0, 0, 0.8);
}
}
.control-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
pointer-events: none;
.play-icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 100rpx;
height: 100rpx;
z-index: 20;
pointer-events: auto;
}
.progress-container {
position: absolute;
bottom: 80rpx;
left: 0;
width: 100%;
padding: 0 30rpx;
box-sizing: border-box;
z-index: 10;
}
.slider {
width: 100%;
}
.time-display {
position: absolute;
bottom: 50rpx;
left: 0;
width: 100%;
display: flex;
justify-content: center;
color: #fff;
font-size: 24rpx;
z-index: 10;
cover-text {
margin: 0 5rpx;
}
}
.time-preview {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: rgba(0, 0, 0, 0.7);
padding: 20rpx 40rpx;
border-radius: 10rpx;
color: #fff;
font-size: 36rpx;
z-index: 30;
}
}
</style>
代码很好,给我改成setup语法糖,再给我加上标题和头像,点赞