Scrapboard.org client
1import { motion, AnimatePresence } from "motion/react";
2import { useState, useEffect } from "react";
3
4interface LikeCounterProps {
5 count: number;
6 className?: string;
7}
8
9function LikeCounter({ count, className = "" }: LikeCounterProps) {
10 const [displayCount, setDisplayCount] = useState<number>(count);
11 const [previousCount, setPreviousCount] = useState<number>(count);
12 const [direction, setDirection] = useState<"up" | "down">("up");
13
14 useEffect(() => {
15 if (count !== displayCount) {
16 setPreviousCount(displayCount);
17 setDirection(count > displayCount ? "up" : "down");
18 setDisplayCount(count);
19 }
20 }, [count, displayCount]);
21
22 return (
23 <div
24 className={`relative inline-block overflow-hidden h-[1.2em] ${className}`}
25 style={{
26 maskImage:
27 "linear-gradient(to bottom, transparent 0%, black 20%, black 80%, transparent 100%)",
28 WebkitMaskImage:
29 "linear-gradient(to bottom, transparent 0%, black 20%, black 80%, transparent 100%)",
30 }}
31 >
32 <motion.div
33 key={`${previousCount}-${displayCount}`}
34 className="flex flex-col"
35 initial={{
36 y: direction === "up" ? "0%" : "-50%",
37 }}
38 animate={{
39 y: direction === "up" ? "-50%" : "0%",
40 }}
41 transition={{
42 type: "spring",
43 damping: 25,
44 stiffness: 200,
45 duration: 0.4,
46 }}
47 >
48 {/* Old number (what we're transitioning FROM) */}
49 <div className="flex items-center justify-center h-[1.2em]">
50 {String(direction == "down" ? displayCount : previousCount)}
51 </div>
52 {/* New number (what we're transitioning TO) */}
53 <div className="flex items-center justify-center h-[1.2em]">
54 {String(direction == "up" ? displayCount : previousCount)}
55 </div>
56 </motion.div>
57 </div>
58 );
59}
60
61export default LikeCounter;