import { ReactNode, useEffect, useState } from 'react';
/**
* Fade in animation wrapper
*/
export function FadeIn({
children,
delay = 0,
duration = 300,
className = '',
}: {
children: ReactNode;
delay?: number;
duration?: number;
className?: string;
}) {
const [visible, setVisible] = useState(false);
useEffect(() => {
const timer = setTimeout(() => setVisible(true), delay);
return () => clearTimeout(timer);
}, [delay]);
return (
{children}
);
}
/**
* Slide in from direction
*/
export function SlideIn({
children,
direction = 'left',
delay = 0,
duration = 300,
distance = 20,
className = '',
}: {
children: ReactNode;
direction?: 'left' | 'right' | 'up' | 'down';
delay?: number;
duration?: number;
distance?: number;
className?: string;
}) {
const [visible, setVisible] = useState(false);
useEffect(() => {
const timer = setTimeout(() => setVisible(true), delay);
return () => clearTimeout(timer);
}, [delay]);
const transforms = {
left: `translateX(${visible ? 0 : -distance}px)`,
right: `translateX(${visible ? 0 : distance}px)`,
up: `translateY(${visible ? 0 : -distance}px)`,
down: `translateY(${visible ? 0 : distance}px)`,
};
return (
{children}
);
}
/**
* Scale in animation
*/
export function ScaleIn({
children,
delay = 0,
duration = 200,
className = '',
}: {
children: ReactNode;
delay?: number;
duration?: number;
className?: string;
}) {
const [visible, setVisible] = useState(false);
useEffect(() => {
const timer = setTimeout(() => setVisible(true), delay);
return () => clearTimeout(timer);
}, [delay]);
return (
{children}
);
}
/**
* Stagger children animations
*/
export function StaggerChildren({
children,
staggerDelay = 50,
initialDelay = 0,
className = '',
}: {
children: ReactNode[];
staggerDelay?: number;
initialDelay?: number;
className?: string;
}) {
return (
{children.map((child, index) => (
{child}
))}
);
}
/**
* Pulse animation (for attention)
*/
export function Pulse({
children,
className = '',
}: {
children: ReactNode;
className?: string;
}) {
return (
{children}
);
}
/**
* Bounce animation
*/
export function Bounce({
children,
className = '',
}: {
children: ReactNode;
className?: string;
}) {
return (
{children}
);
}
/**
* Number counter animation
*/
export function CountUp({
end,
start = 0,
duration = 1000,
decimals = 0,
prefix = '',
suffix = '',
className = '',
}: {
end: number;
start?: number;
duration?: number;
decimals?: number;
prefix?: string;
suffix?: string;
className?: string;
}) {
const [count, setCount] = useState(start);
useEffect(() => {
const startTime = Date.now();
const diff = end - start;
const animate = () => {
const elapsed = Date.now() - startTime;
const progress = Math.min(elapsed / duration, 1);
// Ease out cubic
const eased = 1 - Math.pow(1 - progress, 3);
const current = start + diff * eased;
setCount(current);
if (progress < 1) {
requestAnimationFrame(animate);
}
};
requestAnimationFrame(animate);
}, [end, start, duration]);
return (
{prefix}
{count.toFixed(decimals)}
{suffix}
);
}
/**
* Typing animation for text
*/
export function TypeWriter({
text,
speed = 50,
delay = 0,
className = '',
onComplete,
}: {
text: string;
speed?: number;
delay?: number;
className?: string;
onComplete?: () => void;
}) {
const [displayed, setDisplayed] = useState('');
useEffect(() => {
let index = 0;
const timer = setTimeout(() => {
const interval = setInterval(() => {
setDisplayed(text.slice(0, index + 1));
index++;
if (index >= text.length) {
clearInterval(interval);
onComplete?.();
}
}, speed);
return () => clearInterval(interval);
}, delay);
return () => clearTimeout(timer);
}, [text, speed, delay, onComplete]);
return (
{displayed}
|
);
}