Files
douyin-archive/app/components/HoverVideo.tsx
T
2026-04-28 17:11:42 +08:00

77 lines
1.7 KiB
TypeScript

"use client";
import React, { useCallback, useRef, useState } from "react";
type HoverVideoProps = {
videoUrl: string;
coverUrl?: string | null;
className?: string;
style?: React.CSSProperties;
};
/**
* 鼠标移入才加载视频并自动播放,移出暂停并释放资源;默认显示封面。
*/
export default function HoverVideo({
videoUrl,
coverUrl,
className,
style,
}: HoverVideoProps) {
const [active, setActive] = useState(false);
const videoRef = useRef<HTMLVideoElement | null>(null);
const onEnter = useCallback(() => {
setActive(true);
// 播放在下一帧触发,避免 ref 尚未赋值
requestAnimationFrame(() => {
const v = videoRef.current;
if (!v) return;
v.play().catch(() => {});
});
}, [videoUrl]);
const onLeave = useCallback(() => {
const v = videoRef.current;
if (v) {
try {
v.pause();
v.load();
} catch {}
}
setActive(false);
}, []);
return (
<div
className={className}
style={style}
onMouseEnter={onEnter}
onMouseLeave={onLeave}
>
{/* 封面始终渲染在底层 */}
<img
src={coverUrl || "/placeholder.svg"}
alt="cover"
className="absolute inset-0 w-full h-full object-cover"
draggable={false}
loading="lazy"
/>
{/* 仅在激活后渲染视频;初始不设置 src,防止提前加载 */}
{active ? (
<video
ref={videoRef}
muted
playsInline
loop
autoPlay
src={videoUrl}
preload="none"
className="absolute inset-0 w-full h-full object-cover"
/>
) : null}
</div>
);
}