"use client"; import { useRef, useState, type ReactNode } from "react"; import { motion, useReducedMotion } from "motion/react"; type ShatterPressProps = { children: ReactNode; className?: string; onClick?: () => void; /** Seconds from the break until the pane is whole again. */ reform?: number; /** Cracks from the press point. Each gives two shards. */ spokes?: number; }; type Shard = { clip: string; x: number; y: number; rotate: number }; type Hit = { id: number; shards: Shard[] }; const pct = (p: number[]) => `${p[0].toFixed(1)}% ${p[1].toFixed(1)}%`; function edgePoint(t: number) { const s = ((t % 1) + 1) % 1; const side = Math.floor(s * 4); const f = (s * 4 - side) * 100; return [[f, 0], [100, f], [100 - f, 100], [0, 100 - f]][side]; } /* Glass breaks in a fan from the press point: spokes to the edge, with one ring of cracks across them. */ function breakAt(px: number, py: number, spokes: number): Shard[] { const cuts = Array.from({ length: spokes }, (_, i) => (i + 0.2 + Math.random() * 0.6) / spokes); const ring = 0.3 + Math.random() * 0.2; const mix = (e: number[], k: number) => [px + (e[0] - px) * k, py + (e[1] - py) * k]; const out: Shard[] = []; cuts.forEach((cut, i) => { const after = i === spokes - 1 ? cuts[0] + 1 : cuts[i + 1]; const a = edgePoint(cut); const b = edgePoint(after); // The cuts can straddle a corner. It must stay on the shard or the pane gets a notch. const corners = [0.25, 0.5, 0.75, 1, 1.25].filter((c) => c > cut && c < after).map(edgePoint); const near = [[px, py], mix(a, ring), mix(b, ring)]; const far = [mix(a, ring), a, ...corners, b, mix(b, ring)]; [near, far].forEach((poly, n) => { const cx = poly.reduce((sum, p) => sum + p[0], 0) / poly.length - px; const cy = poly.reduce((sum, p) => sum + p[1], 0) / poly.length - py; const len = Math.hypot(cx, cy) || 1; const push = (n ? 70 : 40) + Math.random() * 70; out.push({ clip: `polygon(${poly.map(pct).join(", ")})`, x: (cx / len) * push, y: (cy / len) * push + 30, rotate: (Math.random() - 0.5) * 140, }); }); }); return out; } /* ShatterPress: a glass pane with a hairline crack. A press breaks it from the exact point you hit, then the pieces fly back. */ export function ShatterPress({ children, className = "rounded-lg border border-white/40 bg-[linear-gradient(135deg,rgba(190,225,255,0.3),rgba(190,225,255,0.09)_55%,rgba(190,225,255,0.22))] px-10 py-4 text-[15px] font-medium text-white", onClick, reform = 1.2, spokes = 11, }: ShatterPressProps) { const reduce = useReducedMotion(); const [hit, setHit] = useState(null); const point = useRef(null); const next = useRef(0); return ( ); }