video-flow-b/components/ui/ImageBlurTransition.tsx
2025-08-01 16:35:23 +08:00

85 lines
2.0 KiB
TypeScript

// Image3DFlipper.tsx
'use client';
import { motion, AnimatePresence } from 'framer-motion';
import { useEffect, useState } from 'react';
type ImageBlurTransitionProps = {
className?: string;
src: string;
alt?: string;
width?: number | string;
height?: number | string;
enableAnimation?: boolean;
};
export default function ImageBlurTransition({
src,
alt = '',
width = 480,
height = 300,
className,
enableAnimation = true
}: ImageBlurTransitionProps) {
const [current, setCurrent] = useState(src);
const [isFlipping, setIsFlipping] = useState(false);
useEffect(() => {
if (src !== current) {
setIsFlipping(true);
const timeout = setTimeout(() => {
setCurrent(src);
setIsFlipping(false);
}, 150); // 时长 = exit 动画时长
return () => clearTimeout(timeout);
}
}, [src, current]);
return (
<div
className={`relative rounded-xl ${className}`}
style={{
width,
height,
perspective: enableAnimation ? 1000 : 'none', // 只在启用动画时提供 3D 深度
}}
>
{enableAnimation ? (
<AnimatePresence mode="wait">
<motion.img
key={current}
src={current}
alt={alt}
className="absolute w-full h-auto object-cover rounded-xl"
initial={{
opacity: 0,
filter: 'blur(8px)',
scale: 1.02,
}}
animate={{
opacity: 1,
filter: 'blur(0px)',
scale: 1,
}}
exit={{
opacity: 0,
filter: 'blur(4px)',
scale: 0.98,
}}
transition={{
duration: 0.3,
ease: 'easeInOut',
}}
/>
</AnimatePresence>
) : (
<img
src={src}
alt={alt}
className="absolute w-full h-auto object-cover rounded-xl"
/>
)}
</div>
);
}