Scrapboard.org client
at labels 61 lines 1.9 kB view raw
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;