From 18b3905c2b3367698e5ef57ab4b1963b31a8df90 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 11 Jul 2025 18:07:43 +0800 Subject: [PATCH] fix: video player --- docs/video-player-usage.md | 388 +++++++++++++++++++++++++++++++++ src/components/VideoPlayer.tsx | 264 +++++++++++++++++----- 2 files changed, 597 insertions(+), 55 deletions(-) create mode 100644 docs/video-player-usage.md diff --git a/docs/video-player-usage.md b/docs/video-player-usage.md new file mode 100644 index 0000000..7139f78 --- /dev/null +++ b/docs/video-player-usage.md @@ -0,0 +1,388 @@ +# 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 +