diff --git a/components/layout/ascii-water-ripple.tsx b/components/layout/ascii-water-ripple.tsx new file mode 100644 index 0000000..cb5c623 --- /dev/null +++ b/components/layout/ascii-water-ripple.tsx @@ -0,0 +1,130 @@ +"use client"; + +import { motion } from "framer-motion"; + +import { SiteLogo } from "@/components/layout/site-logo"; + +type AsciiWaterRippleProps = { + lightLogoUrl?: string | null; + darkLogoUrl?: string | null; + logoAlt: string; +}; + +const glyphs = [".", ":", "+", "*", "o"]; +const rings = [72, 108, 148, 196, 248, 308, 372, 438, 506]; +const pointsPerRing = [16, 22, 30, 38, 48, 58, 68, 80, 92]; + +function buildRipplePoints() { + return rings.flatMap((radius, ringIndex) => { + const points = pointsPerRing[ringIndex]; + + return Array.from({ length: points }, (_, pointIndex) => { + const angle = (Math.PI * 2 * pointIndex) / points; + + return { + id: `${radius}-${pointIndex}`, + x: Math.cos(angle) * radius, + y: Math.sin(angle) * radius, + ringIndex, + pointIndex, + glyph: glyphs[(ringIndex + pointIndex) % glyphs.length], + }; + }); + }); +} + +const ripplePoints = buildRipplePoints(); + +export function AsciiWaterRipple({ + lightLogoUrl, + darkLogoUrl, + logoAlt, +}: AsciiWaterRippleProps) { + return ( +
+ + + + + + + + {ripplePoints.map((point) => ( + + {point.glyph} + + ))} + + + + + +
+ ); +}