# 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(true)}> 播放视频 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(true)}> 播放视频 playerRef.current?.play()}> 播放 playerRef.current?.pause()}> 暂停 playerRef.current?.seek(30)}> 跳转到30秒 截取当前帧 playerRef.current?.fullScreen()}> 全屏 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 ``` ## 🔧 最佳实践 ### 1. 错误处理 ```typescript // 总是提供错误处理回调 { // 记录错误日志 console.error('Video error:', err) // 显示用户友好的错误信息 toast.error('视频播放失败,请重试') // 可选:自动重试 setTimeout(() => { playerRef.current?.reload() }, 2000) }} /> ``` ### 2. 性能优化 ```typescript // 使用 useMemo 缓存回调函数 const handleSourceLoaded = useMemo(() => (player: HTMLVideoElement | null) => { // 处理逻辑 }, []) const handleError = useMemo(() => (err: Error, player: HTMLVideoElement | null) => { // 错误处理逻辑 }, []) ``` ### 3. 资源管理 ```typescript // 组件卸载时清理资源 useEffect(() => { return () => { // VideoPlayer 内部已经处理了资源清理 // 但如果有外部资源需要清理,可以在这里处理 } }, []) ``` ## 🚀 高级用法 ### 视频分析和处理 ```typescript const VideoAnalyzer = () => { const playerRef = useRef(null) const [frames, setFrames] = useState([]) const captureFramesAtInterval = () => { const duration = playerRef.current?.getDuration() if (!duration) return const interval = duration / 10 // 每10%截取一帧 const framePromises = [] for (let i = 0; i < 10; i++) { const time = i * interval framePromises.push( new Promise((resolve) => { playerRef.current?.seek(time) setTimeout(() => { const frame = playerRef.current?.captureFrame() resolve(frame || '') }, 100) }) ) } Promise.all(framePromises).then(setFrames) } return ( 生成视频缩略图 {frames.map((frame, index) => ( ))} ) } ``` --- *重写后的 VideoPlayer 组件提供了更强大的功能和更好的开发体验!*