@@ -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 (
|
||||
<div className="relative flex h-[14rem] w-full items-center justify-center overflow-visible sm:h-[17rem] lg:h-[20rem]">
|
||||
<motion.div
|
||||
animate={{
|
||||
scale: [0.96, 1.08, 0.98],
|
||||
opacity: [0.42, 0.68, 0.48],
|
||||
}}
|
||||
transition={{ duration: 5.2, repeat: Number.POSITIVE_INFINITY, ease: "easeInOut" }}
|
||||
className="absolute left-1/2 top-1/2 h-[16rem] w-[16rem] -translate-x-1/2 -translate-y-1/2 rounded-full bg-primary/16 blur-3xl sm:h-[22rem] sm:w-[22rem] lg:h-[28rem] lg:w-[28rem]"
|
||||
/>
|
||||
|
||||
<motion.div
|
||||
animate={{
|
||||
scale: [1, 1.24, 1.06],
|
||||
opacity: [0.08, 0.18, 0.1],
|
||||
}}
|
||||
transition={{ duration: 7.5, repeat: Number.POSITIVE_INFINITY, ease: "easeInOut" }}
|
||||
className="absolute left-1/2 top-1/2 h-[34rem] w-[34rem] -translate-x-1/2 -translate-y-1/2 rounded-full border border-primary/12"
|
||||
/>
|
||||
|
||||
<motion.div
|
||||
animate={{
|
||||
scale: [0.94, 1.3, 1.02],
|
||||
opacity: [0.06, 0.14, 0.08],
|
||||
}}
|
||||
transition={{ duration: 9.2, repeat: Number.POSITIVE_INFINITY, ease: "easeInOut", delay: 0.4 }}
|
||||
className="absolute left-1/2 top-1/2 h-[44rem] w-[44rem] -translate-x-1/2 -translate-y-1/2 rounded-full border border-primary/10"
|
||||
/>
|
||||
|
||||
<motion.div
|
||||
animate={{
|
||||
scale: [1, 1.035, 0.992, 1],
|
||||
x: [0, 10, -8, 0],
|
||||
y: [0, -6, 4, 0],
|
||||
}}
|
||||
transition={{ duration: 8, repeat: Number.POSITIVE_INFINITY, ease: "easeInOut" }}
|
||||
className="absolute inset-0 flex items-center justify-center overflow-visible"
|
||||
>
|
||||
{ripplePoints.map((point) => (
|
||||
<motion.span
|
||||
key={point.id}
|
||||
initial={{ opacity: 0.14, scale: 0.9 }}
|
||||
animate={{
|
||||
opacity: [0.1, 0.62, 0.16],
|
||||
scale: [0.88, 1.2, 0.94],
|
||||
x: [0, point.ringIndex % 2 === 0 ? 5 : -5, 0],
|
||||
y: [0, point.ringIndex % 3 === 0 ? -4 : 4, 0],
|
||||
}}
|
||||
transition={{
|
||||
duration: 2.6 + point.ringIndex * 0.42,
|
||||
delay: point.pointIndex * 0.012,
|
||||
repeat: Number.POSITIVE_INFINITY,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
className="absolute text-[10px] font-medium leading-none text-primary/75 sm:text-xs"
|
||||
style={{
|
||||
transform: `translate(${point.x}px, ${point.y}px)`,
|
||||
}}
|
||||
>
|
||||
{point.glyph}
|
||||
</motion.span>
|
||||
))}
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
animate={{
|
||||
boxShadow: [
|
||||
"0 0 0 1px hsl(var(--primary) / 0.24), 0 0 42px hsl(var(--primary) / 0.22)",
|
||||
"0 0 0 1px hsl(var(--primary) / 0.34), 0 0 84px hsl(var(--primary) / 0.34)",
|
||||
"0 0 0 1px hsl(var(--primary) / 0.24), 0 0 42px hsl(var(--primary) / 0.22)",
|
||||
],
|
||||
scale: [1, 1.02, 1],
|
||||
y: [0, -3, 0],
|
||||
}}
|
||||
transition={{ duration: 4.8, repeat: Number.POSITIVE_INFINITY, ease: "easeInOut" }}
|
||||
className="relative z-10 flex h-28 w-28 items-center justify-center rounded-full border border-primary/30 bg-background/92 p-2 backdrop-blur-xl sm:h-32 sm:w-32 lg:h-40 lg:w-40"
|
||||
>
|
||||
<SiteLogo
|
||||
lightLogoUrl={lightLogoUrl}
|
||||
darkLogoUrl={darkLogoUrl}
|
||||
alt={logoAlt}
|
||||
priority
|
||||
className="h-full w-full object-center"
|
||||
/>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user