Mini curso · 05

Seguidores con lerp

Tres bloques persiguen al mouse. El primero va pegado; los otros van retrasados. Eso es lerp, la misma idea del anillo del cursor. Aquí va de cero: la fórmula, las coordenadas y el error que hace que “te sigan desde el otro lado de la pantalla”.

JavaScriptlerp requestAnimationFrame⏱ 8 min

Mueve el cursor por la zona. El primer bloque va pegado; baja el "ease" para alargar la cola 👇

mueve el mouse aquí dentro

Con ease alto los bloques casi se pegan al cursor; con ease bajo quedan perezosos y se nota la “cola”. Cada bloque usa un ease un poco menor → efecto de estela.

Qué estás viendo

El anillo azul del cursor y el primer cuadrado usan el mismo número: dónde está el mouse en la ventana. Los otros dos cuadrados no saltan ahí: cada fotograma se acercan un poco al de adelante. Por eso hay cola.

Baja el slider: la cola se alarga. Súbelo: se juntan. Eso no es “más distancia de más”, es menos fracción por frame.

1 · El lerp

Lerp = interpolación lineal. En vez de poner la posición igual al destino, avanzas solo una parte del camino, 60 veces por segundo:

posicion += (destino - posicion) * ease;

ease entre 0 y 1. Cerca de 1: llega ya. Cerca de 0: perezoso. El número del slider es ese ease (en el código aparece aquí: 0.25).

El bucle es requestAnimationFrame: el navegador llama a tu función antes de pintar cada frame. Sin eso, el lerp no se mueve solo.

2 · Dos mapas distintos (aquí estaba el bug)

El mouse te da e.clientX y e.clientY: píxeles desde la esquina de la ventana. El anillo del cursor se mueve con eso, en position: fixed.

Un recuadro del medio de la página es otro mapa: su esquina no es (0, 0) de la ventana. Si haces esto:

x = e.clientX - recuadro.left;
el.style.left = x + 'px';

left se mide desde el bloque padre. Si ese padre es un flex centrado, el (0, 0) de los cuadrados no es la esquina del recuadro: es el centro, junto al texto. Estás sumando “posición del mouse” encima de “ya estaba en el centro”. Resultado: el anillo arriba a la izquierda y el naranja abajo a la derecha. Parece lerp lento. No lo es. Son sistemas de coordenadas distintos.

Subir el ease no lo arregla. “Nacer en el cursor” tampoco, si el origen sigue mal.

3 · La receta que sí pega

Usa el mapa de la ventana, el mismo del anillo:

  1. Guarda clientX / clientY. El recuadro solo sirve para saber si el mouse está dentro (mostrar u ocultar).
  2. Saca los bloques del recuadro (document.body.appendChild). Si se quedan dentro, transform u overflow del padre convierten un position: fixed en coordenadas del recuadro otra vez. Mismo síntoma: siguen de lejos.
  3. Pon position: fixed; top: 0; left: 0 después de moverlos al body, no en el CSS mientras siguen dentro.
  4. Muévelos igual que el cursor: translate(xpx, ypx) translate(-50%, -50%).
let mx = 0, my = 0, dentro = false;

window.addEventListener('mousemove', e => {
  mx = e.clientX;
  my = e.clientY;
  const r = zona.getBoundingClientRect();
  dentro = mx >= r.left && mx <= r.right && my >= r.top && my <= r.bottom;
});
function animar(){
  primero.x = mx;                  // pegado al mouse
  primero.y = my;
  cola.x += (primero.x - cola.x) * ease;  // lerp
  cola.y += (primero.y - cola.y) * ease;
  el.style.transform = `translate(${x}px, ${y}px) translate(-50%,-50%)`;
  requestAnimationFrame(animar);
}

4 · La cola

No hace falta que los tres persigan al mouse. El 0 copia el mouse; el 1 persigue al 0; el 2 persigue al 1, un poco más lento:

bloques[0]  →  mouse          (sin lerp)
bloques[1]  →  bloques[0]     (ease)
bloques[2]  →  bloques[1]     (ease * 0.7)

Así el retraso es real y se ve. El desfase a medio recuadro no era eso.

Por qué importa

El anillo del cursor, la cabeza del búho y estos bloques son la misma línea. Si el lerp “no pega”, primero pregunta: ¿estoy en el mismo mapa que el mouse? Luego ajusta el ease.