"use client"; import { motion, useMotionValue, useSpring, useTransform } from "framer-motion"; import type { MouseEvent, PointerEvent, ReactNode } from "react"; import { cn } from "@/lib/utils"; type PhysicsButtonProps = { href: string; children: ReactNode; className?: string; external?: boolean; onClick?: (event: MouseEvent) => void; }; export default function PhysicsButton({ href, children, className, external = false, onClick }: PhysicsButtonProps) { const pullX = useMotionValue(0); const pullY = useMotionValue(0); const press = useMotionValue(0); const x = useSpring(pullX, { stiffness: 190, damping: 13, mass: 0.42 }); const y = useSpring(pullY, { stiffness: 190, damping: 13, mass: 0.42 }); const pressed = useSpring(press, { stiffness: 520, damping: 18, mass: 0.35 }); const pointerScaleX = useTransform(x, [-30, 0, 30], [1.18, 1, 1.18]); const pointerScaleY = useTransform(y, [-18, 0, 18], [0.9, 1, 0.9]); const tapScaleX = useTransform(pressed, [0, 1], [1, 1.24]); const tapScaleY = useTransform(pressed, [0, 1], [1, 0.82]); const scaleX = useTransform([pointerScaleX, tapScaleX], ([pointerValue, tapValue]) => (pointerValue as number) * (tapValue as number)); const scaleY = useTransform([pointerScaleY, tapScaleY], ([pointerValue, tapValue]) => (pointerValue as number) * (tapValue as number)); const rotate = useTransform(x, [-30, 0, 30], [-2.2, 0, 2.2]); const glowX = useTransform(x, (value) => value * 0.65); const glowY = useTransform(y, (value) => value * 0.65); function handleMove(event: PointerEvent) { if (event.pointerType === "touch") return; const bounds = event.currentTarget.getBoundingClientRect(); const localX = event.clientX - bounds.left - bounds.width / 2; const localY = event.clientY - bounds.top - bounds.height / 2; const xLimit = 30; const yLimit = 18; const strength = 0.42; pullX.set(Math.max(-xLimit, Math.min(xLimit, localX * strength))); pullY.set(Math.max(-yLimit, Math.min(yLimit, localY * strength))); } function release() { pullX.set(0); pullY.set(0); press.set(0); } return ( { event.currentTarget.setPointerCapture?.(event.pointerId); handleMove(event); press.set(1); pullY.set(event.pointerType === "touch" ? 10 : 12); }} onPointerUp={release} onLostPointerCapture={release} whileHover={{ scale: 1.035 }} style={{ x, y, scaleX, scaleY, rotate }} transition={{ type: "spring", stiffness: 520, damping: 18, mass: 0.45 }} className={cn("premium-glass group relative inline-flex touch-manipulation select-none items-center justify-center overflow-hidden will-change-transform", className)} > {children} ); }