NumberOfPosts.tsx (3513B)
1 import { MotionValue, circOut, easeInOut, easeOut, motion, useMotionValue, useScroll, useTime, useTransform } from "framer-motion"; 2 import { ArrowDown, ZapIcon, StickyNote } from "lucide-react"; 3 import { NostrNoteView, ParsedNote } from "@/components/note/NostrNoteView"; 4 import { Npub2024InReviewStats } from "@/pages/purple/2024-in-review/[npub]"; 5 import { cn } from "@/lib/utils"; 6 import { useRef } from "react"; 7 import { Orbitron, Permanent_Marker } from "next/font/google"; 8 import NumberFlow from '@number-flow/react' 9 import { useEffect, useState } from "react"; 10 import { useInterval } from "usehooks-ts"; 11 import Image from "next/image"; 12 13 // const permanentMarker = Permanent_Marker({ weight: "400", subsets: ['latin'] }); 14 15 export function NumberOfPosts({ numberOfPosts, className, style }: { numberOfPosts: number, className?: string, style?: React.CSSProperties }) { 16 const time = useTime() 17 const ref = useRef<HTMLDivElement>(null) 18 const { scrollYProgress } = useScroll({ 19 target: ref 20 }) 21 const [animatedPostAmount, setAnimatedPostAmount] = useState(10); 22 const postAmountProgress = useTransform( 23 scrollYProgress, 24 [0.4, 0.6], 25 [ 26 0.0, 27 1.0 28 ], 29 { 30 clamp: true, 31 ease: circOut 32 } 33 ) 34 const headingOpacity = useTransform( 35 scrollYProgress, 36 [0, 0.4], 37 [0, 1], 38 { 39 clamp: true, 40 ease: easeInOut 41 } 42 ) 43 const secondaryContentOpacity = useTransform( 44 scrollYProgress, 45 [0.4, 0.6], 46 [0, 1], 47 { 48 clamp: true, 49 ease: easeInOut 50 } 51 ) 52 const tertiaryContentOpacity = useTransform( 53 scrollYProgress, 54 [0.8, 1.0], 55 [0, 1], 56 { 57 clamp: true, 58 ease: easeInOut 59 } 60 ) 61 62 useInterval(() => { 63 if (postAmountProgress.get() > 0.0001) { 64 setAnimatedPostAmount(numberOfPosts) 65 } 66 else { 67 setAnimatedPostAmount(10) 68 } 69 }, 500) 70 71 return <div 72 ref={ref} 73 className={cn("container z-30 mx-auto px-4 pt-12 h-full min-h-screen flex flex-col gap-y-4 justify-center items-center", className)} 74 style={style} 75 > 76 <motion.h2 77 className={cn( 78 "text-4xl md:text-6xl text-center text-white font-semibold break-keep tracking-tight z-30", 79 // permanentMarker.className 80 )} 81 style={{ 82 opacity: headingOpacity, 83 }} 84 > 85 You posted 86 </motion.h2> 87 <motion.h3 88 className="flex items-center gap-4 text-3xl md:text-8xl text-center text-white font-semibold break-keep tracking-tight z-30" 89 style={{ 90 opacity: secondaryContentOpacity, 91 }} 92 > 93 <motion.span 94 style={{ opacity: secondaryContentOpacity }} 95 > 96 <StickyNote className="w-8 h-8 md:w-16 md:h-16 text-purple-400" /> 97 </motion.span> 98 <NumberFlow 99 value={animatedPostAmount} 100 continuous={true} 101 transformTiming={{ duration: 1500, easing: "ease-out" }} 102 opacityTiming={{ duration: 1500, easing: "ease-out" }} 103 trend={1} 104 /> 105 <motion.span 106 className="text-purple-400" 107 style={{ opacity: secondaryContentOpacity }} 108 > 109 notes 110 </motion.span> 111 </motion.h3> 112 <motion.div 113 className="space-y-4 z-30" 114 style={{ opacity: tertiaryContentOpacity }} 115 > 116 {numberOfPosts > 200 ? 117 <Image src="/2024-in-review/cat-typing.webp" alt="Cat typing" width={500} height={500} /> 118 : 119 <Image src="/2024-in-review/ostrich-dancing.webp" alt="Cat typing" width={500} height={500} /> 120 } 121 </motion.div> 122 </div> 123 }