"use client"; import * as React from "react"; import { useInViewOnce } from "./useInViewOnce"; const toBits = (value: number, width: number): string => value.toString(2).padStart(width, "0").slice(-width); /** * BinaryTicker: a number shown in binary. The bits rattle when it scrolls into * view and then settle on the value; with `alive` a single bit keeps flipping. */ export const BinaryTicker: React.FC<{ value: number; width?: number; className?: string; alive?: boolean; }> = ({ value, width = 8, className = "", alive = false }) => { const settled = toBits(value, width); const { ref, seen, still } = useInViewOnce(0.6); const [shown, setShown] = React.useState(settled); React.useEffect(() => { if (!seen || still) return; let tick = 0; const timer = window.setInterval(() => { tick += 1; if (tick > 14) { window.clearInterval(timer); setShown(settled); return; } setShown( Array.from(settled) .map((bit, i) => i < tick - 6 ? bit : Math.random() < 0.5 ? "0" : "1", ) .join(""), ); }, 45); return () => window.clearInterval(timer); }, [seen, still, settled]); React.useEffect(() => { if (!alive || !seen || still) return; const timer = window.setInterval(() => { const index = Math.floor(Math.random() * width); setShown((current) => Array.from(current) .map((bit, i) => (i === index ? (bit === "0" ? "1" : "0") : bit)) .join(""), ); window.setTimeout(() => setShown(settled), 260); }, 2400); return () => window.clearInterval(timer); }, [alive, seen, still, width, settled]); return ( ); };