147 lines
4.6 KiB
TypeScript
147 lines
4.6 KiB
TypeScript
"use client";
|
|
|
|
import { useRef, useState } from "react";
|
|
import { Play, Pause, Volume2, VolumeX, Maximize2 } from "lucide-react";
|
|
import { cn } from "@/lib/cn";
|
|
|
|
interface PerformanceVideoProps {
|
|
src?: string;
|
|
poster?: string;
|
|
duration?: string;
|
|
themeColor?: string;
|
|
className?: string;
|
|
}
|
|
|
|
export default function PerformanceVideo({
|
|
src,
|
|
poster,
|
|
duration = "00:15",
|
|
themeColor = "#8b5cf6",
|
|
className,
|
|
}: PerformanceVideoProps) {
|
|
const videoRef = useRef<HTMLVideoElement>(null);
|
|
const [playing, setPlaying] = useState(false);
|
|
const [muted, setMuted] = useState(false);
|
|
|
|
const togglePlay = () => {
|
|
const v = videoRef.current;
|
|
if (!v) return;
|
|
if (v.paused) {
|
|
v.play();
|
|
setPlaying(true);
|
|
} else {
|
|
v.pause();
|
|
setPlaying(false);
|
|
}
|
|
};
|
|
|
|
const toggleMute = () => {
|
|
const v = videoRef.current;
|
|
if (!v) return;
|
|
v.muted = !v.muted;
|
|
setMuted(v.muted);
|
|
};
|
|
|
|
const goFullscreen = () => {
|
|
videoRef.current?.requestFullscreen?.();
|
|
};
|
|
|
|
return (
|
|
<div
|
|
className={cn(
|
|
"relative w-full aspect-video bg-deep rounded-xl overflow-hidden border border-white/[0.08] group",
|
|
className,
|
|
)}
|
|
>
|
|
{src ? (
|
|
<video
|
|
ref={videoRef}
|
|
src={src}
|
|
poster={poster}
|
|
playsInline
|
|
onEnded={() => setPlaying(false)}
|
|
className="absolute inset-0 w-full h-full object-cover"
|
|
/>
|
|
) : (
|
|
<div
|
|
className="absolute inset-0"
|
|
style={{
|
|
background: `radial-gradient(circle at 50% 40%, ${themeColor}55 0%, #1a1638 55%, #08051a 100%)`,
|
|
}}
|
|
>
|
|
{/* 装饰星点 */}
|
|
<div className="absolute inset-0 opacity-50">
|
|
<span className="absolute top-[15%] left-[20%] text-white text-xs">
|
|
✦
|
|
</span>
|
|
<span className="absolute top-[70%] left-[80%] text-purple-300 text-sm">
|
|
✧
|
|
</span>
|
|
<span className="absolute top-[30%] left-[70%] text-white/60 text-[10px]">
|
|
✦
|
|
</span>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* 顶部 15s 标签 */}
|
|
<div className="absolute top-3 left-3 inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full bg-purple-600/90 backdrop-blur-md text-white text-[10px] font-display tracking-widest uppercase shadow-purple-glow z-10">
|
|
▶ 15s Performance
|
|
</div>
|
|
|
|
{/* 时长徽章 */}
|
|
<div className="absolute bottom-3 right-3 px-2 py-0.5 rounded bg-black/65 backdrop-blur-md text-white text-[10px] font-display tracking-wider tabular-nums z-10">
|
|
{duration}
|
|
</div>
|
|
|
|
{/* 中央播放按钮(未播放时) */}
|
|
{!playing && (
|
|
<button
|
|
type="button"
|
|
onClick={togglePlay}
|
|
className="absolute inset-0 flex items-center justify-center z-10 group/play"
|
|
aria-label="播放视频"
|
|
>
|
|
<span className="w-16 h-16 rounded-full bg-white/15 backdrop-blur-md border-2 border-white/40 flex items-center justify-center text-white group-hover/play:scale-110 transition-transform">
|
|
<Play size={22} fill="white" />
|
|
</span>
|
|
</button>
|
|
)}
|
|
|
|
{/* 暂停按钮 + 控制条(播放中) */}
|
|
{playing && (
|
|
<div className="absolute inset-0 flex items-center justify-center z-10 opacity-0 group-hover:opacity-100 transition-opacity bg-black/30">
|
|
<button
|
|
type="button"
|
|
onClick={togglePlay}
|
|
className="w-14 h-14 rounded-full bg-white/15 backdrop-blur-md border-2 border-white/40 flex items-center justify-center text-white hover:scale-105 transition-transform"
|
|
aria-label="暂停"
|
|
>
|
|
<Pause size={20} />
|
|
</button>
|
|
</div>
|
|
)}
|
|
|
|
{/* 右下:音量 / 全屏 */}
|
|
<div className="absolute bottom-3 left-3 flex items-center gap-1.5 z-10 opacity-0 group-hover:opacity-100 transition-opacity">
|
|
<button
|
|
type="button"
|
|
onClick={toggleMute}
|
|
aria-label={muted ? "取消静音" : "静音"}
|
|
className="w-7 h-7 rounded-full bg-black/55 backdrop-blur-md flex items-center justify-center text-white/85 hover:text-white"
|
|
>
|
|
{muted ? <VolumeX size={12} /> : <Volume2 size={12} />}
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={goFullscreen}
|
|
aria-label="全屏"
|
|
className="w-7 h-7 rounded-full bg-black/55 backdrop-blur-md flex items-center justify-center text-white/85 hover:text-white"
|
|
>
|
|
<Maximize2 size={12} />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|