"use client"; import React, { useId, useMemo } from "react"; import { motion } from "framer-motion"; import { cn } from "../utils/cn"; interface PenCircleProps { children: React.ReactNode; delay?: number; className?: string; color?: string; } /** * Ballpoint Pen Circle Component * * Draws a hand-drawn ellipse around children using SVG. * Key: uses preserveAspectRatio="none" so the ellipse stretches * to match the element dimensions regardless of text length. */ function createRng(seed: string) { let s = 0; for (let i = 0; i < seed.length; i++) { s = ((s << 5) - s + seed.charCodeAt(i)) | 0; } return () => { s = (s * 16807 + 0) % 2147483647; return (s & 0x7fffffff) / 0x7fffffff; }; } function generateCirclePath(rng: () => number) { // We draw an ellipse in a 0-100 x 0-100 viewBox. // preserveAspectRatio="none" will stretch it to fit the element. const cx = 50; const cy = 50; // Radii in viewBox units — will be stretched by the element const rx = 50; const ry = 50; // Wobble for organic feel const w = () => (rng() - 0.5) * 6; const k = 0.5522847; const kx = rx * k; const ky = ry * k; // 4 cardinal points with wobble const top = { x: cx + w(), y: cy - ry + w() * 0.5 }; const right = { x: cx + rx + w() * 0.3, y: cy + w() }; const bottom = { x: cx + w(), y: cy + ry + w() * 0.5 }; const left = { x: cx - rx + w() * 0.3, y: cy + w() }; // End slightly offset from start for imperfect closure const endOffX = (rng() - 0.5) * 8; const endOffY = (rng() - 0.5) * 4; return [ `M ${top.x},${top.y}`, `C ${top.x + kx + w()},${top.y + w()} ${right.x + w()},${right.y - ky + w()} ${right.x},${right.y}`, `C ${right.x + w()},${right.y + ky + w()} ${bottom.x + kx + w()},${bottom.y + w()} ${bottom.x},${bottom.y}`, `C ${bottom.x - kx + w()},${bottom.y + w()} ${left.x + w()},${left.y + ky + w()} ${left.x},${left.y}`, `C ${left.x + w()},${left.y - ky + w()} ${top.x - kx + w()},${top.y + w()} ${top.x + endOffX},${top.y + endOffY}`, ].join(" "); } export const PenCircle: React.FC = ({ children, delay = 0, className = "", color = "rgba(37, 99, 235, 0.65)", // Blue ballpoint pen }) => { const id = useId(); const path = useMemo(() => { const rng = createRng(id); return generateCirclePath(rng); }, [id]); return ( {children} ); };