La idea en una frase
Un botón magnético mide cuánto se aleja el cursor del centro del botón y mueve el botón esa misma distancia, pero solo una fracción. Esa fracción es la “fuerza” del imán. Nada de física complicada: pura resta y multiplicación.
const dx = cursorX - centroX;
const dy = cursorY - centroY;
boton.style.transform = `translate(${dx * 0.40}px, ${dy * 0.40}px)`;
Ese 0.40 es exactamente el slider “Fuerza” de arriba —
muévelo y mira cómo cambia el número aquí en el código en tiempo real.
Paso 1 · El HTML
Solo necesitas el botón. Nada especial:
<button class="iman">Atráeme</button>
Paso 2 · El CSS
La clave es will-change: transform (le avisa al navegador que este
elemento se va a mover, para que lo haga fluido en la GPU) y una
transition para que el regreso sea suave.
.iman{
padding:.9rem 1.6rem;
border-radius:999px;
background:#17a2b8; color:#fff;
will-change:transform;
transition:transform .25s ease;
}
Paso 3 · El JavaScript
Escuchamos mousemove sobre el botón, calculamos la distancia al
centro y aplicamos el transform. Al salir (mouseleave)
lo devolvemos a su sitio limpiando el estilo.
const boton = document.querySelector('.iman');
const fuerza = 0.4;
boton.addEventListener('mousemove', e => {
const r = boton.getBoundingClientRect();
const dx = e.clientX - (r.left + r.width / 2);
const dy = e.clientY - (r.top + r.height / 2);
boton.style.transform = `translate(${dx*fuerza}px, ${dy*fuerza}px)`;
});
boton.addEventListener('mouseleave', () => {
boton.style.transform = '';
});
El detalle que lo hace “pro”
El truco final es esa transition en el CSS. Mientras el cursor está
encima, el transform se actualiza tan seguido que el botón parece
pegado al mouse (la transición no alcanza a notarse). Pero al salir, como
limpiamos el transform de golpe, la transición sí actúa y el botón regresa
con un rebote suave. Ese contraste es lo que se siente “vivo”.
Bonus · activarlo solo cuando estás cerca
En el playground de arriba añadimos un radio: el imán solo actúa si el cursor está a menos de X píxeles. Es una línea más:
const dist = Math.hypot(dx, dy);
if (dist < 140) {
boton.style.transform = `translate(${dx*fuerza}px, ${dy*fuerza}px)`;
} else {
boton.style.transform = '';
}
Y eso es todo 🎉
Con distancia + transform + una transición tienes un botón magnético.
La misma idea (mover algo una fracción hacia el cursor) es la base del cursor
personalizado, los seguidores con lerp y hasta la mascota de la portada.