Files
auto-trade/features/dashboard/components/orderbook/AnimatedQuantity.tsx

81 lines
2.5 KiB
TypeScript
Raw Normal View History

2026-02-10 11:16:39 +09:00
import { useEffect, useRef, useState } from "react";
import { cn } from "@/lib/utils";
import { AnimatePresence, motion } from "framer-motion";
interface AnimatedQuantityProps {
value: number;
format?: (val: number) => string;
className?: string;
useColor?: boolean; // 값이 증가하면 빨강, 감소하면 파랑 배경 깜빡임 여부
}
export function AnimatedQuantity({
value,
format = (v) => v.toLocaleString(),
className,
useColor = false,
}: AnimatedQuantityProps) {
const prevValueRef = useRef(value);
const [diff, setDiff] = useState<number | null>(null);
const [flash, setFlash] = useState<"up" | "down" | null>(null);
useEffect(() => {
if (prevValueRef.current !== value) {
const difference = value - prevValueRef.current;
setDiff(difference);
prevValueRef.current = value;
if (difference !== 0) {
setFlash(difference > 0 ? "up" : "down");
const timer = setTimeout(() => {
setDiff(null);
setFlash(null);
}, 1200); // 1.2초 후 초기화
return () => clearTimeout(timer);
}
}
}, [value]);
return (
<div className={cn("relative inline-block tabular-nums", className)}>
{/* Background Flash Effect */}
<AnimatePresence>
{useColor && flash && (
<motion.div
initial={{ opacity: 0.5 }}
animate={{ opacity: 0 }}
exit={{ opacity: 0 }}
transition={{ duration: 1 }}
className={cn(
"absolute inset-0 z-0 rounded-sm",
flash === "up" ? "bg-red-200/50" : "bg-blue-200/50",
)}
/>
)}
</AnimatePresence>
{/* Main Value */}
<span className="relative z-10">{format(value)}</span>
{/* Diff Indicator */}
<AnimatePresence>
{diff !== null && diff !== 0 && (
<motion.span
initial={{ opacity: 1, y: 0, scale: 1 }}
animate={{ opacity: 0, y: -10, scale: 0.9 }}
exit={{ opacity: 0 }}
transition={{ duration: 1.2, ease: "easeOut" }}
className={cn(
"absolute left-full top-0 ml-1 whitespace-nowrap text-[10px] font-bold z-20",
diff > 0 ? "text-red-500" : "text-blue-500",
)}
style={{ pointerEvents: "none" }} // 클릭 방해 방지
>
{diff > 0 ? `+${diff.toLocaleString()}` : diff.toLocaleString()}
</motion.span>
)}
</AnimatePresence>
</div>
);
}