gongxiangp2/components/VideoPlayerModal.tsx

150 lines
4.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'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>
)
}