MVp
This commit is contained in:
109
components/MagneticButton.tsx
Normal file
109
components/MagneticButton.tsx
Normal file
@@ -0,0 +1,109 @@
|
||||
'use client'
|
||||
import { useRef, useEffect } from 'react'
|
||||
import { gsap } from 'gsap'
|
||||
|
||||
interface MagneticButtonProps {
|
||||
children: React.ReactNode
|
||||
className?: string
|
||||
href?: string
|
||||
onClick?: () => void
|
||||
strength?: number
|
||||
}
|
||||
|
||||
export default function MagneticButton({
|
||||
children,
|
||||
className = '',
|
||||
href,
|
||||
onClick,
|
||||
strength = 0.3
|
||||
}: MagneticButtonProps) {
|
||||
const buttonRef = useRef<HTMLElement>(null)
|
||||
const textRef = useRef<HTMLSpanElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const button = buttonRef.current
|
||||
const text = textRef.current
|
||||
if (!button || !text) return
|
||||
|
||||
// Check for reduced motion
|
||||
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
||||
if (prefersReducedMotion) return
|
||||
|
||||
const handleMouseMove = (e: MouseEvent) => {
|
||||
const rect = button.getBoundingClientRect()
|
||||
const x = e.clientX - rect.left - rect.width / 2
|
||||
const y = e.clientY - rect.top - rect.height / 2
|
||||
|
||||
gsap.to(button, {
|
||||
x: x * strength,
|
||||
y: y * strength,
|
||||
duration: 0.3,
|
||||
ease: 'power2.out'
|
||||
})
|
||||
|
||||
gsap.to(text, {
|
||||
x: x * strength * 0.5,
|
||||
y: y * strength * 0.5,
|
||||
duration: 0.3,
|
||||
ease: 'power2.out'
|
||||
})
|
||||
}
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
gsap.to([button, text], {
|
||||
x: 0,
|
||||
y: 0,
|
||||
duration: 0.5,
|
||||
ease: 'elastic.out(1, 0.3)'
|
||||
})
|
||||
}
|
||||
|
||||
const handleMouseDown = () => {
|
||||
gsap.to(button, {
|
||||
scale: 0.98,
|
||||
duration: 0.1,
|
||||
ease: 'power2.out'
|
||||
})
|
||||
}
|
||||
|
||||
const handleMouseUp = () => {
|
||||
gsap.to(button, {
|
||||
scale: 1,
|
||||
duration: 0.2,
|
||||
ease: 'power2.out'
|
||||
})
|
||||
}
|
||||
|
||||
button.addEventListener('mousemove', handleMouseMove)
|
||||
button.addEventListener('mouseleave', handleMouseLeave)
|
||||
button.addEventListener('mousedown', handleMouseDown)
|
||||
button.addEventListener('mouseup', handleMouseUp)
|
||||
|
||||
return () => {
|
||||
button.removeEventListener('mousemove', handleMouseMove)
|
||||
button.removeEventListener('mouseleave', handleMouseLeave)
|
||||
button.removeEventListener('mousedown', handleMouseDown)
|
||||
button.removeEventListener('mouseup', handleMouseUp)
|
||||
}
|
||||
}, [strength])
|
||||
|
||||
const Component = href ? 'a' : 'button'
|
||||
|
||||
return (
|
||||
<Component
|
||||
ref={buttonRef as any}
|
||||
href={href}
|
||||
onClick={onClick}
|
||||
className={`relative inline-block cursor-pointer ${className}`}
|
||||
style={{ willChange: 'transform' }}
|
||||
>
|
||||
<span
|
||||
ref={textRef}
|
||||
className="relative inline-block"
|
||||
style={{ willChange: 'transform' }}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
</Component>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user