ReactJS如何在视频就绪时切换video标签以避免加载黑屏
在NextJS+TypeScript中实现VideoJS双播放器无缝切换消除黑屏
核心实现思路
通过维护两个VideoJS播放器实例,一个处于显示状态,另一个处于隐藏预加载状态:
- 收到服务器
socket的video_data事件时,更新隐藏播放器的视频源 - 待隐藏播放器触发
canplay事件(视频已加载至可播放状态),切换两者的可见性,彻底避免切换过程中的黑屏
完整代码实现
1. 安装依赖
先确保安装必要的包:
npm install video.js @types/video.js socket.io-client # 或 yarn add video.js @types/video.js socket.io-client
2. 播放器组件代码
import React, { useState, useRef, useEffect } from 'react'; import videojs from 'video.js'; import type Player from 'video.js/dist/types/player'; import { io, Socket } from 'socket.io-client'; import 'video.js/dist/video-js.css'; const VideoPlayer: React.FC = () => { // 管理当前激活的播放器索引(0/1) const [activeIdx, setActiveIdx] = useState(0); // 存储两个VideoJS实例的引用 const playerRefs = useRef<(Player | null)[]>([null, null]); // Socket连接实例引用 const socketRef = useRef<Socket | null>(null); useEffect(() => { // 初始化两个播放器 const initPlayers = [0, 1].map(idx => { const videoEl = document.getElementById(`video-player-${idx}`) as HTMLVideoElement; const player = videojs(videoEl, { controls: true, autoplay: false, preload: 'auto', fluid: true, muted: false, // 根据需求调整静音状态 }); // 监听预加载完成事件,触发切换 player.on('canplay', () => { if (idx !== activeIdx) { setActiveIdx(idx); // 重置旧播放器,释放资源 const oldPlayer = playerRefs.current[activeIdx]; if (oldPlayer) { oldPlayer.pause(); oldPlayer.src(''); } } }); return player; }); playerRefs.current = initPlayers; // 初始化Socket连接(替换为你的本地服务器地址) socketRef.current = io('http://localhost:3001'); socketRef.current.on('video_data', (videoData) => { // 获取当前未激活的播放器索引 const inactiveIdx = activeIdx === 0 ? 1 : 0; const targetPlayer = playerRefs.current[inactiveIdx]; if (targetPlayer) { // 更新隐藏播放器的视频源 targetPlayer.src({ src: videoData.url, // 假设videoData返回视频地址 type: videoData.mimeType || 'video/mp4', // 根据实际视频类型调整 }); targetPlayer.load(); } }); // 组件卸载时清理资源 return () => { playerRefs.current.forEach(player => player?.dispose()); socketRef.current?.disconnect(); }; }, [activeIdx]); return ( <div className="video-container"> {[0, 1].map(idx => ( <div key={idx} className={`player-wrapper ${idx === activeIdx ? 'active' : 'hidden'}`} > <video id={`video-player-${idx}`} className="video-js vjs-big-play-centered" /> </div> ))} </div> ); }; export default VideoPlayer;
3. 配套CSS样式
在全局样式文件或组件内联样式中添加:
.video-container { position: relative; width: 100%; max-width: 1200px; margin: 0 auto; aspect-ratio: 16/9; } .player-wrapper { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } .player-wrapper.hidden { display: none; } .player-wrapper.active { display: block; }
关键细节说明
- 预加载时机:始终更新未激活的播放器源,确保切换时视频已完成预加载
- 资源清理:切换后清空旧播放器的源,避免浏览器占用不必要的内存
- Socket稳定性:组件卸载时断开Socket连接,防止内存泄漏
内容的提问来源于stack exchange,提问作者Beki
相关产品推荐
相关产品推荐

