ly0303521

修改页面中缩略图和视频的加载策略,提高加载速度

... ... @@ -12,6 +12,8 @@ interface ImageCardProps {
const ImageCard: React.FC<ImageCardProps> = ({ image, onClick, onLike, currentUser, isVideo = false }) => {
const [isLoaded, setIsLoaded] = useState(false);
const [isHovered, setIsHovered] = useState(false);
const [videoStarted, setVideoStarted] = useState(false);
const videoRef = useRef<HTMLVideoElement>(null);
const handleLike = (e: React.MouseEvent) => {
... ... @@ -20,60 +22,75 @@ const ImageCard: React.FC<ImageCardProps> = ({ image, onClick, onLike, currentUs
};
const handleMouseEnter = () => {
if (videoRef.current) {
videoRef.current.currentTime = 0;
videoRef.current.play().catch(e => console.error("Video play failed", e));
}
setIsHovered(true);
// Video will be played via autoPlay once rendered
}
const handleMouseLeave = () => {
if (videoRef.current) {
videoRef.current.pause();
videoRef.current.currentTime = videoRef.current.duration || 0;
}
setIsHovered(false);
setVideoStarted(false);
}
const handleVideoMetadata = () => {
if (videoRef.current) {
videoRef.current.currentTime = videoRef.current.duration || 0;
}
}
// If it's a video with a thumbnail, we want to show it immediately (via poster)
// instead of waiting for the video file to load metadata.
// Content is considered "ready" if the main image/video is loaded
// OR if we have a thumbnail to show for a video.
const showContent = isLoaded || (isVideo && !!image.thumbnail);
return (
<div
className="group relative mb-4 break-inside-avoid rounded-2xl overflow-hidden bg-gray-200 cursor-zoom-in shadow-sm hover:shadow-xl transition-all duration-300"
className="group relative mb-4 break-inside-avoid rounded-2xl overflow-hidden bg-gray-100 cursor-zoom-in shadow-sm hover:shadow-xl transition-all duration-300"
onClick={() => onClick(image)}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
{/* Placeholder / Skeleton */}
{!showContent && (
<div className="absolute inset-0 bg-gray-200 animate-pulse min-h-[200px]" />
<div className="absolute inset-0 bg-gray-200 animate-pulse min-h-[150px]" />
)}
{/* Main Content */}
{isVideo ? (
<video
ref={videoRef}
src={image.url}
poster={image.thumbnail} // Use thumbnail as poster
className={`w-full h-auto object-cover transition-opacity duration-300 ${showContent ? 'opacity-100' : 'opacity-0'}`}
onLoadedMetadata={handleVideoMetadata}
onLoadedData={() => setIsLoaded(true)}
loop
muted
playsInline
preload="metadata"
/>
<div className="relative w-full overflow-hidden bg-gray-100">
{/* Always show thumbnail first */}
<img
src={image.thumbnail || (isVideo ? '' : image.url)}
alt={image.prompt}
className={`w-full h-auto object-cover transition-opacity duration-500 ${videoStarted ? 'opacity-0' : 'opacity-100'} ${!image.thumbnail && isVideo ? 'opacity-0' : ''}`}
onLoad={() => setIsLoaded(true)}
loading="lazy"
/>
{isVideo && !image.thumbnail && !videoStarted && (
<div className="absolute inset-0 flex items-center justify-center bg-gray-200 text-gray-400">
<Video size={32} />
</div>
)}
{/* Load video only on hover */}
{isHovered && (
<video
ref={videoRef}
src={image.url}
className={`absolute inset-0 w-full h-full object-cover transition-opacity duration-300 ${videoStarted ? 'opacity-100' : 'opacity-0'}`}
onLoadedData={() => setVideoStarted(true)}
autoPlay
loop
muted
playsInline
/>
)}
{/* Video Indicator Icon (When not hovered) */}
{!isHovered && (
<div className="absolute top-3 left-3 p-1.5 bg-black/30 backdrop-blur-md rounded-lg text-white/90">
<Video size={14} />
</div>
)}
</div>
) : (
<img
src={image.url}
alt={image.prompt}
className={`w-full h-auto object-cover transition-transform duration-700 ease-in-out group-hover:scale-105 ${showContent ? 'opacity-100' : 'opacity-0'}`}
className={`w-full h-auto object-cover transition-transform duration-700 ease-in-out group-hover:scale-105 ${isLoaded ? 'opacity-100' : 'opacity-0'}`}
onLoad={() => setIsLoaded(true)}
loading="lazy"
/>
... ...