# VideoPlayer 组件使用指南 基于 demo.md 重写的 VideoPlayer 组件使用说明 ## 🎯 设计特点 ### 参考 demo.md 的优秀设计模式 1. **useImperativeHandle**: 暴露组件方法供外部调用 2. **清晰的接口定义**: TypeScript 接口规范 3. **完善的事件处理**: 加载、播放、错误等事件回调 4. **加载状态管理**: 统一的加载状态显示 5. **资源清理机制**: 防止内存泄漏 6. **错误处理回调**: 可自定义错误处理逻辑 ## 📚 接口定义 ### VideoPlayerRef ```typescript export interface VideoPlayerRef { seek(time: number): void; // 跳转到指定时间 pause(): void; // 暂停播放 play(): void; // 开始播放 getCurrentTime(): number; // 获取当前播放时间 isPaused(): boolean; // 是否暂停状态 getDuration(): number; // 获取视频总时长 captureFrame(): string | undefined; // 截取当前帧 fullScreen(): void; // 全屏播放 reload(): void; // 重新加载视频 } ``` ### 事件回调接口 ```typescript export interface OnSourceLoaded { (player: HTMLVideoElement | null): void; } export interface OnPlayerError { (err: Error, player: HTMLVideoElement | null): void; } export interface OnPlayerTimeChange { (player: HTMLVideoElement | null): void; } ``` ### 组件Props ```typescript interface VideoPlayerProps { videoPath: string // 视频文件路径 isOpen: boolean // 是否打开播放器 onClose: () => void // 关闭回调 title?: string // 播放器标题 onSourceLoaded?: OnSourceLoaded // 视频加载完成回调 onError?: OnPlayerError // 错误处理回调 onTimeChange?: OnPlayerTimeChange // 时间变化回调 } ``` ## 🔧 基本使用 ### 1. 简单使用 ```tsx import VideoPlayer from '@/components/VideoPlayer' function MyComponent() { const [isPlayerOpen, setIsPlayerOpen] = useState(false) const [videoPath, setVideoPath] = useState('') return ( <> setIsPlayerOpen(false)} title="我的视频" /> ) } ``` ### 2. 带事件处理的使用 ```tsx import VideoPlayer, { VideoPlayerRef } from '@/components/VideoPlayer' function AdvancedVideoPlayer() { const playerRef = useRef(null) const [isPlayerOpen, setIsPlayerOpen] = useState(false) const [videoPath, setVideoPath] = useState('') const handleSourceLoaded = (player: HTMLVideoElement | null) => { console.log('视频加载完成:', { duration: player?.duration, videoWidth: player?.videoWidth, videoHeight: player?.videoHeight }) } const handleError = (err: Error, player: HTMLVideoElement | null) => { console.error('视频播放错误:', err.message) // 可以显示自定义错误提示 alert(`播放失败: ${err.message}`) } const handleTimeChange = (player: HTMLVideoElement | null) => { if (player) { console.log(`播放进度: ${player.currentTime}/${player.duration}`) } } const handleCaptureFrame = () => { const frameData = playerRef.current?.captureFrame() if (frameData) { console.log('截取帧数据:', frameData) // 可以保存或显示截图 } } return ( <>
setIsPlayerOpen(false)} title="高级视频播放器" onSourceLoaded={handleSourceLoaded} onError={handleError} onTimeChange={handleTimeChange} /> ) } ``` ## 🎮 控制方法 ### 播放控制 ```typescript // 播放视频 playerRef.current?.play() // 暂停视频 playerRef.current?.pause() // 检查播放状态 const isPaused = playerRef.current?.isPaused() ``` ### 时间控制 ```typescript // 跳转到指定时间(秒) playerRef.current?.seek(120) // 跳转到2分钟 // 获取当前播放时间 const currentTime = playerRef.current?.getCurrentTime() // 获取视频总时长 const duration = playerRef.current?.getDuration() ``` ### 高级功能 ```typescript // 截取当前帧 const frameDataUrl = playerRef.current?.captureFrame() if (frameDataUrl) { // 可以用于显示缩略图或保存截图 const img = new Image() img.src = frameDataUrl document.body.appendChild(img) } // 全屏播放 playerRef.current?.fullScreen() // 重新加载视频 playerRef.current?.reload() ``` ## 🔄 事件处理 ### 视频加载完成 ```typescript const handleSourceLoaded = (player: HTMLVideoElement | null) => { if (player) { console.log('视频信息:', { duration: player.duration, width: player.videoWidth, height: player.videoHeight, readyState: player.readyState }) // 可以在这里设置默认音量、播放速度等 player.volume = 0.8 player.playbackRate = 1.0 } } ``` ### 错误处理 ```typescript const handleError = (err: Error, player: HTMLVideoElement | null) => { console.error('播放错误:', err) // 根据错误类型进行不同处理 if (err.message.includes('文件不存在')) { // 处理文件不存在的情况 showNotification('视频文件不存在,请检查文件路径') } else if (err.message.includes('格式不支持')) { // 处理格式不支持的情况 showNotification('视频格式不支持,请转换为MP4格式') } else { // 通用错误处理 showNotification('视频播放失败,请重试') } } ``` ### 播放进度监听 ```typescript const handleTimeChange = (player: HTMLVideoElement | null) => { if (player) { const progress = (player.currentTime / player.duration) * 100 // 更新进度条 setPlayProgress(progress) // 记录观看进度 saveWatchProgress(videoId, player.currentTime) // 在特定时间点触发事件 if (Math.floor(player.currentTime) === 60) { console.log('播放到1分钟了!') } } } ``` ## 🎨 样式定制 ### 加载状态自定义 组件内置了加载状态显示,使用 Loader2 图标和半透明背景。如需自定义,可以修改组件内的样式: ```tsx {isLoading && (
加载中...
)} ``` ### 视频容器样式 ```tsx