video-flow-b/components/ui/ImageBlurTransition.tsx
2025-08-09 16:41:05 +08:00

83 lines
1.9 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 w-full h-fit ${className}`}
style={{
perspective: enableAnimation ? 1000 : 'none', // 只在启用动画时提供 3D 深度
}}
>
{enableAnimation ? (
<AnimatePresence mode="wait">
<motion.img
key={current}
src={current}
alt={alt}
className="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="w-full h-auto object-cover rounded-xl"
/>
)}
</div>
);
}