从零用java实现 小红书 springboot vue uniapp(13)模仿抖音视频切换

从零用java实现 小红书 springboot vue uniapp(13)实战:用Swiper+Video打造抖音式丝滑视频流

移动端演示 http://8.146.211.120:8081/#/

管理端演示 http://8.146.211.120:8088/#/

项目整体介绍及演示

前言

在上一篇文章中我们实现了视频笔记的发布功能,现在,我们将攻克一个更核心的体验功能:创建一个像抖音、快手那样的全屏、可上下滑动切换的视频信息流 。这不仅仅是UI的堆砌,背后涉及到 swipervideo 组件的深度联动、视频生命周期的精细化管理、以及数据预加载等关键技术。本文将详细拆解其实现过程。


核心技术实现

我们的目标是当用户向上或向下滑动时,上一个视频能自动暂停,下一个视频能自动播放,并且列表能无限滚动加载。我们将围绕 videoDetail.vue 文件来展开。

1. 基础布局:Swiper 与 Video 的结合

首先,我们搭建页面的骨架。外层使用 swiper 组件作为滑动容器,并设置其 vertical="true" 来实现垂直滚动。内部通过 v-for 循环渲染 swiper-item,每个 swiper-item 中放置一个全屏的 video 组件。

html 复制代码
<template>
    <swiper class="swiper-container" :vertical="true" @change="handleSwiperChange" :current="currentIndex">
        <swiper-item v-for="(video, index) in videoData" :key="video.noteId">
            <view class="video-container">
                <video 
                    class="video" 
                    :src="video.videoUrl"
                    :id="`video_${index}`" 
                    :loop="true"
                    :controls="false"
                    @timeupdate="handleTimeUpdate">
                </video>
                <!-- 其他UI元素,如点赞、评论按钮等 -->
            </view>
        </swiper-item>
    </swiper>
</template>
  • @change="handleSwiperChange":这是实现功能的核心,每当滑动切换视频时,该事件会被触发。
  • :id="'video_' + index":为每个video组件设置一个唯一的ID,这是后续通过代码精确控制视频播放/暂停的关键。
2. 视频播放控制:uni.createVideoContext

要用代码控制视频,我们必须先获取到每个视频的实例,即 VideoContext

我们在 data 中创建一个数组 videoContexts: [] 用于存储这些实例。然后,在页面数据加载并渲染完成后,初始化它们。

javascript 复制代码
// script
export default {
    data() {
        return {
            videoData: [],
            currentIndex: 0,
            videoContexts: []
        };
    },
    onReady() {
        // onReady生命周期确保了组件已渲染
        this.initVideoContexts();
    },
    methods: {
        initVideoContexts() {
            this.videoContexts = []; // 清空旧实例
            this.videoData.forEach((item, index) => {
                // 通过 video 的 id 创建并存储 context
                this.videoContexts[index] = uni.createVideoContext(`video_${index}`, this);
            });
            // 自动播放第一个视频
            if (this.videoContexts[this.currentIndex]) {
                this.videoContexts[this.currentIndex].play();
            }
        }
    }
}
3. 核心交互:滑动切换与自动播放

所有的关键都发生在 handleSwiperChange 方法中。当用户滑动 swiper 时,我们需要:

  1. 暂停上一个正在播放的视频。
  2. 播放当前显示的新视频。
javascript 复制代码
methods: {
    handleSwiperChange(event) {
        const { current } = event.detail;
        
        // 记录上一个视频的索引
        const previousIndex = this.currentIndex;

        // 暂停上一个视频
        if (this.videoContexts[previousIndex]) {
            this.videoContexts[previousIndex].pause();
        }

        // 更新当前视频的索引
        this.currentIndex = current;
        this.paused = false; // 重置手动暂停状态

        // 延时播放当前视频,确保滑动手势完成
        setTimeout(() => {
            if (this.videoContexts[this.currentIndex]) {
                this.videoContexts[this.currentIndex].play();
            }
        }, 250);
    },
}
4. 数据流:无限滚动与预加载

为了实现"刷不完"的效果,我们需要在用户快要滑到底部时,提前加载下一页的数据。

数据加载:

我们封装一个 loadVideos 方法,通过分页参数(page, pageSize)从后端获取视频列表,并追加到 videoData 数组中。

触发加载:

我们在 swiper 组件上监听 @scrolltolower 事件(在uniapp中,需要自己根据 currentIndex 模拟)。当用户滑动到倒数N个视频时(例如 preloadThreshold = 2),就调用 loadVideos

javascript 复制代码
// 在 handleSwiperChange 方法的最后调用
handleSwiperChange(event) {
    // ...上面的播放/暂停逻辑...
    
    // 检查是否需要加载更多视频
    this.checkAndLoadMore();
},

methods: {
    checkAndLoadMore() {
        // 当滑动到倒数第 preloadThreshold 个视频时,加载更多
        const isNearEnd = this.currentIndex >= this.videoData.length - this.preloadThreshold;
        
        if (isNearEnd && this.hasMore && !this.loading) {
            this.loadVideos(); // 该方法内部会请求API并追加数据
        }
    },

    loadVideos() {
        if(this.loading || !this.hasMore) return;
        this.loading = true;
        uni.app.get('/auth/getVideoNotes', { page: this.page, limit: this.pageSize }, '', (res => {
            // ...处理返回的数据...
            const newVideos = res.data.records || [];
            this.videoData = [...this.videoData, ...newVideos];
            this.hasMore = newVideos.length >= this.pageSize;
            this.page++;
            this.loading = false;
            
            // !! 关键:数据更新后,需要重新初始化新增的 video context
            this.$nextTick(() => {
                this.initVideoContexts();
            });
        }));
    }
}

注意: 每次加载新数据后,videoData 数组都变了,因此需要重新调用 initVideoContexts 来为新加入的视频创建 VideoContext 实例。


通过以上四步,我们就完整地实现了一个功能强大且体验流畅的抖音式视频流。

代码地址
https://gitee.com/ddeatrr/springboot_vue_xhs

相关推荐
企微自动化8 小时前
企业微信二次开发:深度解析外部群主动推送的实现路径
java·开发语言·企业微信
_修铁路的8 小时前
【Poi-tl】 Word模板填充导出
java·word·poi-tl
武子康8 小时前
Java-216 RocketMQ 4.5.1 在 JDK9+ 从0到1全流程启动踩坑全解:脚本兼容修复(GC 参数/CLASSPATH/ext.dirs)
java·大数据·分布式·消息队列·系统架构·rocketmq·java-rocketmq
austin流川枫8 小时前
🔥MySQL的大表优化方案 (实战分享)
java·mysql·性能优化
码界奇点8 小时前
基于Spring Boot和Vue.js的视频点播管理系统设计与实现
java·vue.js·spring boot·后端·spring·毕业设计·源代码管理
爱吃山竹的大肚肚8 小时前
MySQL 支持的各类索引
java·数据库·sql·mysql·spring·spring cloud
程序员水自流8 小时前
MySQL常用内置函数详细介绍
java·数据库·mysql
廋到被风吹走8 小时前
【Spring】Spring Boot详细介绍
java·spring boot·spring
期待のcode8 小时前
Java中的继承
java·开发语言
czlczl200209258 小时前
基于 Spring Boot 权限管理 RBAC 模型
前端·javascript·spring boot