blob: 9183c1146bd8af810c441e09dccd5310e963a37a [file] [log] [blame]
import React, { useState, useRef } from 'react'
import { Play, Pause, Volume2, VolumeX, Maximize2, X } from 'lucide-react'
const VideoPreview = ({ src, poster, onClose, className = '', style = {} }) => {
const videoRef = useRef(null)
const [isPlaying, setIsPlaying] = useState(false)
const [isMuted, setIsMuted] = useState(false)
const [isFullscreen, setIsFullscreen] = useState(false)
const [duration, setDuration] = useState(0)
const [currentTime, setCurrentTime] = useState(0)
const togglePlay = () => {
if (videoRef.current) {
if (isPlaying) {
videoRef.current.pause()
} else {
videoRef.current.play()
}
setIsPlaying(!isPlaying)
}
}
const toggleMute = () => {
if (videoRef.current) {
videoRef.current.muted = !isMuted
setIsMuted(!isMuted)
}
}
const toggleFullscreen = () => {
if (videoRef.current) {
if (!isFullscreen) {
if (videoRef.current.requestFullscreen) {
videoRef.current.requestFullscreen()
}
} else {
if (document.exitFullscreen) {
document.exitFullscreen()
}
}
setIsFullscreen(!isFullscreen)
}
}
const handleTimeUpdate = () => {
if (videoRef.current) {
setCurrentTime(videoRef.current.currentTime)
}
}
const handleLoadedMetadata = () => {
if (videoRef.current) {
setDuration(videoRef.current.duration)
}
}
const handleSeek = (e) => {
if (videoRef.current) {
const rect = e.currentTarget.getBoundingClientRect()
const clickX = e.clientX - rect.left
const newTime = (clickX / rect.width) * duration
videoRef.current.currentTime = newTime
setCurrentTime(newTime)
}
}
const formatTime = (time) => {
const minutes = Math.floor(time / 60)
const seconds = Math.floor(time % 60)
return `${minutes}:${seconds.toString().padStart(2, '0')}`
}
return (
<div className={`video-preview ${className}`} style={style}>
<div className="video-container" style={{ position: 'relative', borderRadius: 8, overflow: 'hidden' }}>
<video
ref={videoRef}
src={src}
poster={poster}
onTimeUpdate={handleTimeUpdate}
onLoadedMetadata={handleLoadedMetadata}
onPlay={() => setIsPlaying(true)}
onPause={() => setIsPlaying(false)}
style={{ width: '100%', height: '100%', objectFit: 'cover' }}
preload="metadata"
/>
{/* 视频控制层 */}
<div
className="video-controls"
style={{
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
background: 'linear-gradient(transparent, rgba(0,0,0,0.7))',
padding: '20px 12px 12px',
opacity: 1,
transition: 'opacity 0.3s'
}}
>
{/* 进度条 */}
<div
className="progress-bar"
style={{
height: 4,
background: 'rgba(255,255,255,0.3)',
borderRadius: 2,
marginBottom: 8,
cursor: 'pointer'
}}
onClick={handleSeek}
>
<div
style={{
height: '100%',
background: '#fff',
borderRadius: 2,
width: `${duration ? (currentTime / duration) * 100 : 0}%`,
transition: 'width 0.1s'
}}
/>
</div>
{/* 控制按钮 */}
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<button
onClick={togglePlay}
style={{
background: 'none',
border: 'none',
color: 'white',
cursor: 'pointer',
padding: 4,
borderRadius: 4
}}
>
{isPlaying ? <Pause size={20} /> : <Play size={20} />}
</button>
<button
onClick={toggleMute}
style={{
background: 'none',
border: 'none',
color: 'white',
cursor: 'pointer',
padding: 4,
borderRadius: 4
}}
>
{isMuted ? <VolumeX size={18} /> : <Volume2 size={18} />}
</button>
<span style={{ color: 'white', fontSize: 12 }}>
{formatTime(currentTime)} / {formatTime(duration)}
</span>
</div>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<button
onClick={toggleFullscreen}
style={{
background: 'none',
border: 'none',
color: 'white',
cursor: 'pointer',
padding: 4,
borderRadius: 4
}}
>
<Maximize2 size={18} />
</button>
{onClose && (
<button
onClick={onClose}
style={{
background: 'none',
border: 'none',
color: 'white',
cursor: 'pointer',
padding: 4,
borderRadius: 4
}}
>
<X size={18} />
</button>
)}
</div>
</div>
</div>
</div>
</div>
)
}
export default VideoPreview