UNPKG

lightswind

Version:

A collection of beautifully crafted React Components, Blocks & Templates for Modern Developers. Create stunning web applications effortlessly by using our 160+ professional and animated react components.

55 lines (54 loc) 3.04 kB
// @ts-nocheck "use client"; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { useRef, useState } from "react"; import { motion, useSpring, useTransform } from "framer-motion"; import { cn } from "../../lib/utils"; export function MagneticButton({ children, strength = 0.4, radius = 80, variant = "primary", size = "md", onClick, className, }) { const buttonRef = useRef(null); const [isHovered, setIsHovered] = useState(false); // Spring config — snappy but elastic const springConfig = { stiffness: 200, damping: 18, mass: 0.6 }; const rawX = useSpring(0, springConfig); const rawY = useSpring(0, springConfig); // Inner text moves slightly less than the container (parallax depth) const textX = useTransform(rawX, (v) => v * 0.4); const textY = useTransform(rawY, (v) => v * 0.4); const handleMouseMove = (e) => { const rect = buttonRef.current?.getBoundingClientRect(); if (!rect) return; const centerX = rect.left + rect.width / 2; const centerY = rect.top + rect.height / 2; const distX = e.clientX - centerX; const distY = e.clientY - centerY; const dist = Math.sqrt(distX ** 2 + distY ** 2); if (dist < radius) { rawX.set(distX * strength); rawY.set(distY * strength); setIsHovered(true); } else { rawX.set(0); rawY.set(0); setIsHovered(false); } }; const handleMouseLeave = () => { rawX.set(0); rawY.set(0); setIsHovered(false); }; const variants = { primary: "bg-primary text-primary-foreground hover:bg-primary/90 shadow-lg shadow-primary/20", outline: "border-2 border-foreground text-foreground hover:bg-foreground/5", ghost: "text-foreground hover:bg-foreground/8", dark: "bg-foreground text-background shadow-lg", }; const sizes = { sm: "h-9 px-5 text-sm rounded-full", md: "h-12 px-8 text-base rounded-full", lg: "h-14 px-12 text-lg rounded-full", }; return (_jsx("div", { ref: buttonRef, onMouseMove: handleMouseMove, onMouseLeave: handleMouseLeave, style: { display: "inline-flex", padding: radius * 0.25 }, children: _jsxs(motion.button, { type: "button", onClick: onClick, style: { x: rawX, y: rawY }, animate: { scale: isHovered ? 1.04 : 1 }, transition: { type: "spring", stiffness: 300, damping: 20 }, className: cn("relative inline-flex items-center justify-center font-semibold tracking-tight transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring overflow-hidden", variants[variant], sizes[size], className), children: [_jsx(motion.span, { animate: { opacity: isHovered ? 1 : 0 }, transition: { duration: 0.25 }, className: "pointer-events-none absolute inset-0 rounded-full bg-white/10" }), _jsx(motion.span, { style: { x: textX, y: textY }, className: "relative z-10 flex items-center gap-2", children: children })] }) })); }