97 lines
1.8 KiB
TypeScript
97 lines
1.8 KiB
TypeScript
import { motion } from 'framer-motion';
|
|
import type { ReactNode } from 'react';
|
|
|
|
interface AnimatedContainerProps {
|
|
children: ReactNode;
|
|
delay?: number;
|
|
className?: string;
|
|
direction?: 'up' | 'left' | 'right' | 'fade';
|
|
}
|
|
|
|
const variants = {
|
|
up: {
|
|
hidden: { opacity: 0, y: 30 },
|
|
visible: { opacity: 1, y: 0 },
|
|
},
|
|
left: {
|
|
hidden: { opacity: 0, x: -30 },
|
|
visible: { opacity: 1, x: 0 },
|
|
},
|
|
right: {
|
|
hidden: { opacity: 0, x: 30 },
|
|
visible: { opacity: 1, x: 0 },
|
|
},
|
|
fade: {
|
|
hidden: { opacity: 0 },
|
|
visible: { opacity: 1 },
|
|
},
|
|
};
|
|
|
|
export default function AnimatedContainer({
|
|
children,
|
|
delay = 0,
|
|
className = '',
|
|
direction = 'up',
|
|
}: AnimatedContainerProps) {
|
|
return (
|
|
<motion.div
|
|
initial="hidden"
|
|
whileInView="visible"
|
|
viewport={{ once: true, margin: '-50px' }}
|
|
transition={{ duration: 0.55, delay, ease: 'easeOut' }}
|
|
variants={variants[direction]}
|
|
className={className}
|
|
>
|
|
{children}
|
|
</motion.div>
|
|
);
|
|
}
|
|
|
|
// Stagger container for grid cards
|
|
export function StaggerContainer({
|
|
children,
|
|
className = '',
|
|
}: {
|
|
children: ReactNode;
|
|
className?: string;
|
|
}) {
|
|
return (
|
|
<motion.div
|
|
initial="hidden"
|
|
animate="visible"
|
|
variants={{
|
|
hidden: {},
|
|
visible: {
|
|
transition: {
|
|
staggerChildren: 0.08,
|
|
},
|
|
},
|
|
}}
|
|
className={className}
|
|
>
|
|
{children}
|
|
</motion.div>
|
|
);
|
|
}
|
|
|
|
// Individual stagger item
|
|
export function StaggerItem({
|
|
children,
|
|
className = '',
|
|
}: {
|
|
children: ReactNode;
|
|
className?: string;
|
|
}) {
|
|
return (
|
|
<motion.div
|
|
variants={{
|
|
hidden: { opacity: 0, y: 24 },
|
|
visible: { opacity: 1, y: 0, transition: { duration: 0.45, ease: 'easeOut' } },
|
|
}}
|
|
className={className}
|
|
>
|
|
{children}
|
|
</motion.div>
|
|
);
|
|
}
|