Magnetic Cursor Effect

An interactive React component that creates magnetic attraction effects when cursor approaches elements with smooth spring physics.

1
'use client';
2
import { useRef, useEffect, useState, useCallback, ReactNode } from 'react';
3
import { motion, useMotionValue, useSpring } from 'framer-motion';
4
import { cn } from '@/lib/utils';
5
6
interface MagneticProps {
7
magneticDistance?: number;
8
strength?: number;
9
stiffness?: number;
10
damping?: number;
11
children: ReactNode | ((isHovering: boolean) => ReactNode);
12
}
13
14
function Magnetic({
15
magneticDistance = 200,
16
strength = 0.9,
17
stiffness = 80,
18
damping = 10,
19
children,
20
}: MagneticProps) {
21
const ref = useRef<HTMLDivElement>(null);
22
const x = useMotionValue(0);
23
const y = useMotionValue(0);
24
const springX = useSpring(x, { stiffness, damping });
25
const springY = useSpring(y, { stiffness, damping });
26
const [isHovering, setIsHovering] = useState(false);
27
28
const onMouseMove = useCallback(
29
(e: MouseEvent) => {
30
if (!ref.current) return;
31
const rect = ref.current.getBoundingClientRect();
32
const cx = rect.left + rect.width / 2;
33
const cy = rect.top + rect.height / 2;
34
const dx = e.clientX - cx;
35
const dy = e.clientY - cy;
36
const dist = Math.sqrt(dx * dx + dy * dy);
37
38
if (dist < magneticDistance) {
39
const pull = Math.pow(1 - dist / magneticDistance, 0.5);
40
x.set(dx * pull * strength);
41
y.set(dy * pull * strength);
42
setIsHovering(true);
43
} else {
44
x.set(0);
45
y.set(0);
46
setIsHovering(false);
47
}
48
},
49
[x, y, magneticDistance, strength]
50
);
51
52
useEffect(() => {
53
window.addEventListener('mousemove', onMouseMove);
54
return () => window.removeEventListener('mousemove', onMouseMove);
55
}, [onMouseMove]);
56
57
return (
58
<motion.div
59
ref={ref}
60
style={{ x: springX, y: springY, display: 'inline-block' }}
61
>
62
{typeof children === 'function' ? children(isHovering) : children}
63
</motion.div>
64
);
65
}
66
67
export default function MagneticCursor() {
68
return (
69
<div className='grid place-items-center h-full'>
70
<Magnetic>
71
{(hovering) => (
72
<button
73
onClick={() => alert('clicked!')}
74
className={cn(
75
`
76
w-36 h-36 border-red-500 border rounded-full text-xs tracking-widest uppercase font-semibold
77
cursor-pointer transition-all duration-400 outline-none
78
79
`,
80
hovering
81
? 'shadow-[0_0_60px_rgba(255,140,120,0.45),0_0_120px_rgba(255,140,120,0.15)]'
82
: 'shadow-[0_4px_28px_rgba(255,140,120,0.22)]',
83
hovering && 'scale-105 bg-red-500 text-white'
84
)}
85
>
86
ENTER
87
</button>
88
)}
89
</Magnetic>
90
</div>
91
);
92
}
93

Installtion

1
npm install motion