197 lines
7.3 KiB
TypeScript
197 lines
7.3 KiB
TypeScript
|
|
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",
|
||
|
|
}),
|
||
|
|
};
|