Spaces:
Sleeping
Sleeping
| 'use client'; | |
| import { useRef, useState } from 'react'; | |
| import { motion } from 'framer-motion'; | |
| /** | |
| * MagneticButton — a button that gravitates toward the cursor. | |
| * | |
| * As the mouse gets close, the button slightly moves toward it. | |
| * Creates a premium, tactile feel that reduces click friction. | |
| */ | |
| export function MagneticButton({ children, className = '', onClick, disabled }: { | |
| children: React.ReactNode; | |
| className?: string; | |
| onClick?: () => void; | |
| disabled?: boolean; | |
| }) { | |
| const ref = useRef<HTMLButtonElement>(null); | |
| const [offset, setOffset] = useState({ x: 0, y: 0 }); | |
| const handleMouseMove = (e: React.MouseEvent) => { | |
| if (disabled) return; | |
| const el = ref.current; | |
| if (!el) return; | |
| const rect = el.getBoundingClientRect(); | |
| const centerX = rect.left + rect.width / 2; | |
| const centerY = rect.top + rect.height / 2; | |
| const dx = (e.clientX - centerX) * 0.3; // 30% magnetic pull | |
| const dy = (e.clientY - centerY) * 0.3; | |
| setOffset({ x: dx, y: dy }); | |
| }; | |
| const handleMouseLeave = () => { | |
| setOffset({ x: 0, y: 0 }); | |
| }; | |
| return ( | |
| <motion.button | |
| ref={ref} | |
| onMouseMove={handleMouseMove} | |
| onMouseLeave={handleMouseLeave} | |
| onClick={onClick} | |
| disabled={disabled} | |
| animate={{ x: offset.x, y: offset.y }} | |
| transition={{ type: 'spring', stiffness: 300, damping: 20 }} | |
| className={className} | |
| > | |
| {children} | |
| </motion.button> | |
| ); | |
| } | |