ERP-node/frontend/lib/animations/animations.ts

197 lines
7.3 KiB
TypeScript
Raw Normal View History

2025-10-01 16:15:53 +09:00
export interface AnimationConfig {
duration?: number;
delay?: number;
easing?: string;
fillMode?: "forwards" | "backwards" | "both" | "none";
iterationCount?: number | "infinite";
}
export const animations = {
// 페이드 애니메이션
fadeIn: (config: AnimationConfig = {}) => ({
animation: `fadeIn ${config.duration || 300}ms ${config.easing || "ease-in-out"} ${config.delay || 0}ms ${config.fillMode || "forwards"}`,
"@keyframes fadeIn": {
"0%": { opacity: 0 },
"100%": { opacity: 1 },
},
}),
fadeOut: (config: AnimationConfig = {}) => ({
animation: `fadeOut ${config.duration || 300}ms ${config.easing || "ease-in-out"} ${config.delay || 0}ms ${config.fillMode || "forwards"}`,
"@keyframes fadeOut": {
"0%": { opacity: 1 },
"100%": { opacity: 0 },
},
}),
// 슬라이드 애니메이션
slideInFromLeft: (config: AnimationConfig = {}) => ({
animation: `slideInFromLeft ${config.duration || 400}ms ${config.easing || "ease-out"} ${config.delay || 0}ms ${config.fillMode || "forwards"}`,
"@keyframes slideInFromLeft": {
"0%": { transform: "translateX(-100%)", opacity: 0 },
"100%": { transform: "translateX(0)", opacity: 1 },
},
}),
slideInFromRight: (config: AnimationConfig = {}) => ({
animation: `slideInFromRight ${config.duration || 400}ms ${config.easing || "ease-out"} ${config.delay || 0}ms ${config.fillMode || "forwards"}`,
"@keyframes slideInFromRight": {
"0%": { transform: "translateX(100%)", opacity: 0 },
"100%": { transform: "translateX(0)", opacity: 1 },
},
}),
slideInFromTop: (config: AnimationConfig = {}) => ({
animation: `slideInFromTop ${config.duration || 400}ms ${config.easing || "ease-out"} ${config.delay || 0}ms ${config.fillMode || "forwards"}`,
"@keyframes slideInFromTop": {
"0%": { transform: "translateY(-100%)", opacity: 0 },
"100%": { transform: "translateY(0)", opacity: 1 },
},
}),
slideInFromBottom: (config: AnimationConfig = {}) => ({
animation: `slideInFromBottom ${config.duration || 400}ms ${config.easing || "ease-out"} ${config.delay || 0}ms ${config.fillMode || "forwards"}`,
"@keyframes slideInFromBottom": {
"0%": { transform: "translateY(100%)", opacity: 0 },
"100%": { transform: "translateY(0)", opacity: 1 },
},
}),
// 스케일 애니메이션
scaleIn: (config: AnimationConfig = {}) => ({
animation: `scaleIn ${config.duration || 300}ms ${config.easing || "ease-out"} ${config.delay || 0}ms ${config.fillMode || "forwards"}`,
"@keyframes scaleIn": {
"0%": { transform: "scale(0)", opacity: 0 },
"100%": { transform: "scale(1)", opacity: 1 },
},
}),
scaleOut: (config: AnimationConfig = {}) => ({
animation: `scaleOut ${config.duration || 300}ms ${config.easing || "ease-in"} ${config.delay || 0}ms ${config.fillMode || "forwards"}`,
"@keyframes scaleOut": {
"0%": { transform: "scale(1)", opacity: 1 },
"100%": { transform: "scale(0)", opacity: 0 },
},
}),
// 바운스 애니메이션
bounce: (config: AnimationConfig = {}) => ({
animation: `bounce ${config.duration || 600}ms ${config.easing || "ease-in-out"} ${config.delay || 0}ms ${config.iterationCount || 1}`,
"@keyframes bounce": {
"0%, 20%, 53%, 80%, 100%": { transform: "translate3d(0,0,0)" },
"40%, 43%": { transform: "translate3d(0,-30px,0)" },
"70%": { transform: "translate3d(0,-15px,0)" },
"90%": { transform: "translate3d(0,-4px,0)" },
},
}),
// 회전 애니메이션
rotate: (config: AnimationConfig = {}) => ({
animation: `rotate ${config.duration || 1000}ms ${config.easing || "linear"} ${config.delay || 0}ms ${config.iterationCount || "infinite"}`,
"@keyframes rotate": {
"0%": { transform: "rotate(0deg)" },
"100%": { transform: "rotate(360deg)" },
},
}),
// 펄스 애니메이션
pulse: (config: AnimationConfig = {}) => ({
animation: `pulse ${config.duration || 1000}ms ${config.easing || "ease-in-out"} ${config.delay || 0}ms ${config.iterationCount || "infinite"}`,
"@keyframes pulse": {
"0%": { transform: "scale(1)", opacity: 1 },
"50%": { transform: "scale(1.05)", opacity: 0.8 },
"100%": { transform: "scale(1)", opacity: 1 },
},
}),
// 타이핑 애니메이션
typewriter: (config: AnimationConfig = {}) => ({
animation: `typewriter ${config.duration || 2000}ms ${config.easing || "steps(40, end)"} ${config.delay || 0}ms ${config.fillMode || "forwards"}`,
"@keyframes typewriter": {
"0%": { width: "0" },
"100%": { width: "100%" },
},
}),
// 글로우 애니메이션
glow: (config: AnimationConfig = {}) => ({
animation: `glow ${config.duration || 2000}ms ${config.easing || "ease-in-out"} ${config.delay || 0}ms ${config.iterationCount || "infinite"}`,
"@keyframes glow": {
"0%, 100%": { boxShadow: "0 0 5px rgba(59, 130, 246, 0.5)" },
"50%": { boxShadow: "0 0 20px rgba(59, 130, 246, 0.8), 0 0 30px rgba(59, 130, 246, 0.6)" },
},
}),
// 웨이브 애니메이션
wave: (config: AnimationConfig = {}) => ({
animation: `wave ${config.duration || 1000}ms ${config.easing || "ease-in-out"} ${config.delay || 0}ms ${config.iterationCount || "infinite"}`,
"@keyframes wave": {
"0%, 100%": { transform: "rotate(0deg)" },
"25%": { transform: "rotate(20deg)" },
"75%": { transform: "rotate(-10deg)" },
},
}),
};
// 애니메이션 조합
export const animationCombos = {
// 페이지 전환
pageTransition: (direction: "left" | "right" | "up" | "down" = "right") => {
const slideAnimation = direction === "left" ? animations.slideInFromLeft :
direction === "right" ? animations.slideInFromRight :
direction === "up" ? animations.slideInFromTop :
animations.slideInFromBottom;
return {
...slideAnimation({ duration: 500, easing: "cubic-bezier(0.4, 0, 0.2, 1)" }),
...animations.fadeIn({ duration: 500, delay: 100 }),
};
},
// 모달 등장
modalEnter: () => ({
...animations.scaleIn({ duration: 300, easing: "cubic-bezier(0.34, 1.56, 0.64, 1)" }),
...animations.fadeIn({ duration: 300 }),
}),
// 모달 퇴장
modalExit: () => ({
...animations.scaleOut({ duration: 200, easing: "cubic-bezier(0.4, 0, 1, 1)" }),
...animations.fadeOut({ duration: 200 }),
}),
// 버튼 클릭
buttonClick: () => ({
...animations.scaleIn({ duration: 150, easing: "ease-out" }),
}),
// 성공 알림
successNotification: () => ({
...animations.slideInFromRight({ duration: 400, easing: "ease-out" }),
...animations.bounce({ duration: 600, delay: 200 }),
}),
// 로딩 스피너
loadingSpinner: () => ({
...animations.rotate({ duration: 1000, iterationCount: "infinite" }),
}),
// 호버 효과
hoverLift: () => ({
transition: "transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out",
"&:hover": {
transform: "translateY(-2px)",
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
},
}),
// 타이핑 효과
typingText: (text: string, speed: number = 50) => ({
...animations.typewriter({ duration: text.length * speed }),
overflow: "hidden",
whiteSpace: "nowrap",
borderRight: "2px solid",
borderRightColor: "currentColor",
}),
};