150 lines
4.6 KiB
TypeScript
150 lines
4.6 KiB
TypeScript
'use client'
|
||
|
||
import { useState, useRef, useEffect } from 'react'
|
||
|
||
interface VideoPlayerModalProps {
|
||
videoSrc: string
|
||
onClose: () => void
|
||
}
|
||
|
||
export default function VideoPlayerModal({ videoSrc, onClose }: VideoPlayerModalProps) {
|
||
const [isLoading, setIsLoading] = useState(true)
|
||
const [isBuffering, setIsBuffering] = useState(false)
|
||
const [loadProgress, setLoadProgress] = useState(0)
|
||
const [canPlay, setCanPlay] = useState(false)
|
||
const videoRef = useRef<HTMLVideoElement>(null)
|
||
|
||
useEffect(() => {
|
||
// 组件挂载时,视频开始加载
|
||
setIsLoading(true)
|
||
setCanPlay(false)
|
||
setLoadProgress(0)
|
||
}, [videoSrc])
|
||
|
||
const handleLoadStart = () => {
|
||
console.log('[VideoPlayer] 开始加载视频')
|
||
setIsLoading(true)
|
||
setCanPlay(false)
|
||
setLoadProgress(0)
|
||
}
|
||
|
||
const handleLoadedMetadata = () => {
|
||
console.log('[VideoPlayer] 视频元数据加载完成')
|
||
setIsLoading(false)
|
||
}
|
||
|
||
const handleProgress = (e: React.SyntheticEvent<HTMLVideoElement>) => {
|
||
const video = e.currentTarget
|
||
if (video.buffered.length > 0 && video.duration > 0) {
|
||
const bufferedEnd = video.buffered.end(video.buffered.length - 1)
|
||
const progress = (bufferedEnd / video.duration) * 100
|
||
setLoadProgress(progress)
|
||
console.log(`[VideoPlayer] 加载进度: ${progress.toFixed(1)}%`)
|
||
}
|
||
}
|
||
|
||
const handleCanPlay = () => {
|
||
console.log('[VideoPlayer] 视频可以播放')
|
||
setCanPlay(true)
|
||
setIsLoading(false)
|
||
}
|
||
|
||
const handleCanPlayThrough = () => {
|
||
console.log('[VideoPlayer] 视频可以流畅播放')
|
||
setIsLoading(false)
|
||
setIsBuffering(false)
|
||
}
|
||
|
||
const handleWaiting = () => {
|
||
console.log('[VideoPlayer] 视频缓冲中...')
|
||
setIsBuffering(true)
|
||
}
|
||
|
||
const handlePlaying = () => {
|
||
console.log('[VideoPlayer] 视频开始播放')
|
||
setIsBuffering(false)
|
||
}
|
||
|
||
const handleError = (e: React.SyntheticEvent<HTMLVideoElement>) => {
|
||
console.error('[VideoPlayer] 视频加载错误:', e)
|
||
setIsLoading(false)
|
||
}
|
||
|
||
return (
|
||
<div
|
||
className="fixed inset-0 z-[100] bg-black/80 flex items-center justify-center p-4"
|
||
onClick={onClose}
|
||
>
|
||
<div
|
||
className="relative w-full max-w-5xl"
|
||
style={{
|
||
maxHeight: 'min(750px, calc(100vh - 2rem))',
|
||
maxWidth: 'min(1333px, calc(100vw - 2rem))'
|
||
}}
|
||
onClick={(e) => e.stopPropagation()}
|
||
>
|
||
<div
|
||
className="relative w-full bg-black rounded-apple shadow-apple-lg overflow-hidden"
|
||
style={{
|
||
maxHeight: 'min(750px, calc(100vh - 2rem))',
|
||
aspectRatio: '16/9',
|
||
width: '100%',
|
||
height: 'auto'
|
||
}}
|
||
>
|
||
{/* 加载状态指示器 */}
|
||
{(isLoading || isBuffering) && (
|
||
<div className="absolute inset-0 flex items-center justify-center bg-black/50 z-10">
|
||
<div className="text-center">
|
||
<div className="inline-block animate-spin rounded-full h-12 w-12 border-4 border-white border-t-transparent mb-4"></div>
|
||
<p className="text-white text-sm">
|
||
{isLoading ? '正在加载视频...' : '视频缓冲中...'}
|
||
</p>
|
||
{loadProgress > 0 && (
|
||
<div className="mt-2 w-64 bg-gray-700 rounded-full h-2 overflow-hidden">
|
||
<div
|
||
className="bg-blue-500 h-full transition-all duration-300"
|
||
style={{ width: `${loadProgress}%` }}
|
||
/>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
<video
|
||
ref={videoRef}
|
||
src={videoSrc}
|
||
controls
|
||
autoPlay
|
||
preload="auto"
|
||
playsInline
|
||
className="w-full h-full object-contain"
|
||
style={{
|
||
maxHeight: 'min(750px, calc(100vh - 2rem))',
|
||
width: '100%',
|
||
height: 'auto'
|
||
}}
|
||
onLoadStart={handleLoadStart}
|
||
onLoadedMetadata={handleLoadedMetadata}
|
||
onProgress={handleProgress}
|
||
onCanPlay={handleCanPlay}
|
||
onCanPlayThrough={handleCanPlayThrough}
|
||
onWaiting={handleWaiting}
|
||
onPlaying={handlePlaying}
|
||
onError={handleError}
|
||
/>
|
||
</div>
|
||
<button
|
||
className="absolute -top-3 -right-3 bg-white text-gray-800 rounded-full w-8 h-8 shadow flex items-center justify-center hover:bg-gray-100 transition-colors z-10"
|
||
onClick={onClose}
|
||
aria-label="关闭"
|
||
>
|
||
×
|
||
</button>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|