155 lines
4.4 KiB
TypeScript
155 lines
4.4 KiB
TypeScript
|
|
"use client";
|
||
|
|
|
||
|
|
import React, { useState, useEffect } from "react";
|
||
|
|
import { animations, animationCombos, AnimationConfig } from "@/lib/animations/animations";
|
||
|
|
|
||
|
|
interface AnimatedComponentProps {
|
||
|
|
children: React.ReactNode;
|
||
|
|
animation?: keyof typeof animations;
|
||
|
|
combo?: keyof typeof animationCombos;
|
||
|
|
config?: AnimationConfig;
|
||
|
|
trigger?: "mount" | "hover" | "click" | "visible";
|
||
|
|
delay?: number;
|
||
|
|
className?: string;
|
||
|
|
style?: React.CSSProperties;
|
||
|
|
}
|
||
|
|
|
||
|
|
export const AnimatedComponent: React.FC<AnimatedComponentProps> = ({
|
||
|
|
children,
|
||
|
|
animation = "fadeIn",
|
||
|
|
combo,
|
||
|
|
config = {},
|
||
|
|
trigger = "mount",
|
||
|
|
delay = 0,
|
||
|
|
className = "",
|
||
|
|
style = {},
|
||
|
|
}) => {
|
||
|
|
const [isVisible, setIsVisible] = useState(false);
|
||
|
|
const [isAnimating, setIsAnimating] = useState(false);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (trigger === "mount") {
|
||
|
|
const timer = setTimeout(() => {
|
||
|
|
setIsVisible(true);
|
||
|
|
setIsAnimating(true);
|
||
|
|
}, delay);
|
||
|
|
|
||
|
|
return () => clearTimeout(timer);
|
||
|
|
}
|
||
|
|
}, [trigger, delay]);
|
||
|
|
|
||
|
|
const handleMouseEnter = () => {
|
||
|
|
if (trigger === "hover") {
|
||
|
|
setIsAnimating(true);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const handleMouseLeave = () => {
|
||
|
|
if (trigger === "hover") {
|
||
|
|
setIsAnimating(false);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const handleClick = () => {
|
||
|
|
if (trigger === "click") {
|
||
|
|
setIsAnimating(true);
|
||
|
|
setTimeout(() => setIsAnimating(false), config.duration || 300);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const getAnimationStyle = () => {
|
||
|
|
if (combo) {
|
||
|
|
return animationCombos[combo]();
|
||
|
|
}
|
||
|
|
|
||
|
|
if (animation) {
|
||
|
|
return animations[animation](config);
|
||
|
|
}
|
||
|
|
|
||
|
|
return {};
|
||
|
|
};
|
||
|
|
|
||
|
|
const animationStyle = isAnimating ? getAnimationStyle() : {};
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div
|
||
|
|
className={className}
|
||
|
|
style={{
|
||
|
|
...style,
|
||
|
|
...animationStyle,
|
||
|
|
}}
|
||
|
|
onMouseEnter={handleMouseEnter}
|
||
|
|
onMouseLeave={handleMouseLeave}
|
||
|
|
onClick={handleClick}
|
||
|
|
>
|
||
|
|
{children}
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
// 특화된 애니메이션 컴포넌트들
|
||
|
|
export const FadeIn: React.FC<Omit<AnimatedComponentProps, "animation">> = (props) => (
|
||
|
|
<AnimatedComponent {...props} animation="fadeIn" />
|
||
|
|
);
|
||
|
|
|
||
|
|
export const SlideInFromLeft: React.FC<Omit<AnimatedComponentProps, "animation">> = (props) => (
|
||
|
|
<AnimatedComponent {...props} animation="slideInFromLeft" />
|
||
|
|
);
|
||
|
|
|
||
|
|
export const SlideInFromRight: React.FC<Omit<AnimatedComponentProps, "animation">> = (props) => (
|
||
|
|
<AnimatedComponent {...props} animation="slideInFromRight" />
|
||
|
|
);
|
||
|
|
|
||
|
|
export const SlideInFromTop: React.FC<Omit<AnimatedComponentProps, "animation">> = (props) => (
|
||
|
|
<AnimatedComponent {...props} animation="slideInFromTop" />
|
||
|
|
);
|
||
|
|
|
||
|
|
export const SlideInFromBottom: React.FC<Omit<AnimatedComponentProps, "animation">> = (props) => (
|
||
|
|
<AnimatedComponent {...props} animation="slideInFromBottom" />
|
||
|
|
);
|
||
|
|
|
||
|
|
export const ScaleIn: React.FC<Omit<AnimatedComponentProps, "animation">> = (props) => (
|
||
|
|
<AnimatedComponent {...props} animation="scaleIn" />
|
||
|
|
);
|
||
|
|
|
||
|
|
export const Bounce: React.FC<Omit<AnimatedComponentProps, "animation">> = (props) => (
|
||
|
|
<AnimatedComponent {...props} animation="bounce" />
|
||
|
|
);
|
||
|
|
|
||
|
|
export const Pulse: React.FC<Omit<AnimatedComponentProps, "animation">> = (props) => (
|
||
|
|
<AnimatedComponent {...props} animation="pulse" />
|
||
|
|
);
|
||
|
|
|
||
|
|
export const Glow: React.FC<Omit<AnimatedComponentProps, "animation">> = (props) => (
|
||
|
|
<AnimatedComponent {...props} animation="glow" />
|
||
|
|
);
|
||
|
|
|
||
|
|
// 조합 애니메이션 컴포넌트들
|
||
|
|
export const PageTransition: React.FC<Omit<AnimatedComponentProps, "combo"> & { direction?: "left" | "right" | "up" | "down" }> = ({ direction, ...props }) => (
|
||
|
|
<AnimatedComponent {...props} combo="pageTransition" />
|
||
|
|
);
|
||
|
|
|
||
|
|
export const ModalEnter: React.FC<Omit<AnimatedComponentProps, "combo">> = (props) => (
|
||
|
|
<AnimatedComponent {...props} combo="modalEnter" />
|
||
|
|
);
|
||
|
|
|
||
|
|
export const ModalExit: React.FC<Omit<AnimatedComponentProps, "combo">> = (props) => (
|
||
|
|
<AnimatedComponent {...props} combo="modalExit" />
|
||
|
|
);
|
||
|
|
|
||
|
|
export const ButtonClick: React.FC<Omit<AnimatedComponentProps, "combo">> = (props) => (
|
||
|
|
<AnimatedComponent {...props} combo="buttonClick" trigger="click" />
|
||
|
|
);
|
||
|
|
|
||
|
|
export const SuccessNotification: React.FC<Omit<AnimatedComponentProps, "combo">> = (props) => (
|
||
|
|
<AnimatedComponent {...props} combo="successNotification" />
|
||
|
|
);
|
||
|
|
|
||
|
|
export const LoadingSpinner: React.FC<Omit<AnimatedComponentProps, "combo">> = (props) => (
|
||
|
|
<AnimatedComponent {...props} combo="loadingSpinner" />
|
||
|
|
);
|
||
|
|
|
||
|
|
export const HoverLift: React.FC<Omit<AnimatedComponentProps, "combo">> = (props) => (
|
||
|
|
<AnimatedComponent {...props} combo="hoverLift" trigger="hover" />
|
||
|
|
);
|