wandres.dev
GSAP VI · ScrollTrigger: el modelo

markers: convertir la abstracción en cuatro líneas de colores

Cómo leer los cuatro marcadores, qué significa cada uno, cómo personalizarlos e identificarlos, y el catálogo de diagnósticos que permiten hacer de un vistazo.

⏱ 16 min

markers: true es la única línea de ScrollTrigger que deberías escribir en el mismo momento en que escribes trigger, antes incluso de decidir qué animación vas a hacer. No es una ayuda para principiantes: es el visualizador de un cálculo invisible, y sin él estás ajustando valores a ciegas y atribuyendo a la suerte comportamientos que tienen una explicación geométrica exacta. La curva de aprendizaje de este plugin se acorta a la mitad en cuanto entiendes que los cuatro marcadores no son adornos, sino la representación literal de los dos números que la instancia ha calculado.

🎯 Al terminar esta lección sabrás
  • Identificar los cuatro marcadores y saber a qué objeto pertenece cada par.
  • Personalizar color, tamaño y sangrado para distinguir instancias solapadas.
  • Usar id para etiquetar marcadores y localizar instancias por código.
  • Diagnosticar los cinco fallos más comunes leyendo solo la posición de los marcadores.

Cuatro marcadores, dos parejas

Al activar markers: true aparecen cuatro etiquetas. Es tentador pensar que son “el principio y el final”, pero son cuatro porque hay dos objetos implicados, exactamente los mismos dos de la sintaxis de start y end.

Dos de ellos, scroller-start y scroller-end, están anclados al viewport: no se mueven al hacer scroll, porque representan las posiciones del scroller que has escrito en la segunda mitad de cada valor. Si pones start: "top 85%", el marcador scroller-start estará clavado al 85% de la altura de la pantalla, siempre.

Los otros dos, start y end, están anclados al documento: se desplazan con el contenido al hacer scroll, porque representan los puntos del elemento disparador que has escrito en la primera mitad. El start verde está en el punto del trigger indicado por la primera mitad de start; el end rojo en el punto indicado por la primera mitad de end, medido sobre el endTrigger si lo has declarado.

La instancia se activa exactamente cuando el marcador del documento cruza el marcador del scroller de su mismo color. Ver esa colisión ocurrir a la vez que la animación arranca es lo que convierte el modelo abstracto en algo que puedes predecir.

gsap.from(".tarjeta", {
  autoAlpha: 0,
  y: 60,
  scrollTrigger: {
    trigger: ".tarjeta",
    start: "top 85%",
    end: "bottom 40%",
    markers: true,
  },
});

Un detalle que ahorra mucho tiempo: cuando hay pin, el end del documento se sitúa donde acaba el intervalo antes de que se añada el espaciador. Los marcadores siguen midiendo el documento real, así que si ves el end en un sitio que no cuadra con la altura visual de la página, casi siempre es porque un fijado ha alargado el documento y estás comparando manzanas con naranjas.

Personalizar para no perderse

En una página con quince instancias, markers: true produce un amasijo ilegible en el que todas las etiquetas se pisan. La forma de objeto arregla eso.

ScrollTrigger.create({
  trigger: ".seccion-3",
  start: "top center",
  end: "bottom center",
  id: "seccion-3",
  markers: {
    startColor: "#a6e3a1",
    endColor: "#f38ba8",
    fontSize: "13px",
    fontWeight: "bold",
    indent: 160,
  },
});

Las cinco propiedades son las únicas que existen: startColor, endColor, fontSize, fontWeight e indent. Los valores por defecto son "green", "red", "16px", "normal" y 0.

indent es la más infravalorada: desplaza las etiquetas de esa instancia hacia dentro un número de píxeles, de modo que dos instancias con indent distinto dejan de solaparse y puedes leer las cuatro etiquetas de cada una. Con tres o cuatro instancias en la misma zona, dar a cada una un indent escalonado de 0, 120, 240 y 360 convierte el amasijo en algo legible.

id merece capítulo aparte porque hace dos cosas a la vez. Aparece en las etiquetas de los marcadores, de modo que sabes cuál es cuál sin contar, y sirve para recuperar la instancia por código con ScrollTrigger.getById("seccion-3"). Poner id a todas tus instancias cuesta cero y convierte la consola en una herramienta de depuración de verdad.

💡
Marcadores para todas las instancias de golpe

Durante una sesión de depuración no quieres ir añadiendo markers: true instancia por instancia. ScrollTrigger.defaults({ markers: true }) lo activa para todas las que se creen a partir de esa línea, así que colocada antes de tu código de animación te ilumina la página entera. En producción, la misma llamada con markers: false no hace falta: basta con no ejecutarla.

Los cinco diagnósticos de un vistazo

Casi todos los problemas de ScrollTrigger tienen una firma visual reconocible en los marcadores. Merece la pena memorizarlas.

El end está por encima del start. El intervalo es de longitud cero o negativa, así que la instancia se activa y se desactiva en el mismo píxel. Ocurre cuando el elemento disparador es más bajo que la distancia entre las dos posiciones del scroller: con start: "top 20%" y end: "bottom 80%", un elemento de 100px de alto tiene el final por encima del principio. La solución no es tocar los porcentajes, es usar un end relativo como "+=400".

Los marcadores no están donde está el elemento. Casi siempre significa que el elemento está dentro de un contenedor con transform o con will-change, y ScrollTrigger mide en el flujo del documento mientras que el navegador lo pinta desplazado. También ocurre con position: sticky en un ancestro.

Los marcadores están donde deben, pero la animación se dispara tarde. Las medidas se tomaron antes de que algo cambiara la altura del documento: imágenes sin width y height declarados, una fuente que reflowea, contenido inyectado. Un ScrollTrigger.refresh() después del evento load lo confirma en dos segundos.

Los marcadores saltan al hacer scroll rápido. Hay una fijación por encima cuya distancia todavía no se había sumado a las posiciones de esta instancia. Es el fallo de orden de creación: las instancias deben crearse en el mismo orden en que aparecen en la página.

No aparece ningún marcador. O el selector de trigger no encuentra nada —comprueba st.trigger en la consola—, o el elemento tiene display: none, o tiene content-visibility en auto o en hidden, que para estos efectos se comporta como si no existiera y hace imposible calcular su posición.

// Auditoria completa de la pagina en una sola linea de consola
console.table(
  ScrollTrigger.getAll().map((t) => ({
    id: t.vars.id ?? "sin-id",
    start: Math.round(t.start),
    end: Math.round(t.end),
    longitud: Math.round(t.end - t.start),
    trigger: t.trigger?.className ?? "ninguno",
  }))
);

Esa tabla es el complemento textual de los marcadores y detecta lo que el ojo no ve: instancias con longitud cero, instancias cuyo trigger es undefined porque el selector falló, e instancias duplicadas con las mismas posiciones que delatan que tu código de inicialización se ejecutó dos veces.

Los marcadores no son una ayuda didáctica: son la interfaz de un compilador que normalmente no tiene ninguna

Hay una asimetría curiosa en las herramientas que usamos a diario. Cuando escribes CSS, el inspector te muestra el modelo de caja calculado, la cascada resuelta y qué regla ganó. Cuando escribes una consulta SQL, el motor te da un plan de ejecución. Cuando escribes una configuración de ScrollTrigger, estás alimentando a un compilador que traduce restricciones geométricas a dos enteros, y ese compilador normalmente no te enseña su salida: solo ves el efecto. Los marcadores son el EXPLAIN de ese compilador, y esa es exactamente la razón de que quien los usa aprenda el plugin en una tarde y quien no los usa siga peleándose con él meses después. La consecuencia práctica va más allá de depurar: cambia tu forma de escribir. Con los marcadores puestos, dejas de escribir start: "top 80%" y comprobar si el resultado te gusta; empiezas a decidir dónde quieres que esté la línea verde y a escribir el valor que la pone ahí. Es la diferencia entre programar por prueba y error y programar por construcción. Y hay un corolario incómodo: si en tu proyecto los marcadores están desactivados y nadie los enciende, es probable que la mitad de vuestras configuraciones tengan valores que nadie eligió a conciencia, heredados de un copiar y pegar y ajustados hasta que dejaron de molestar. Encenderlos una vez sobre una página en producción suele ser una experiencia reveladora y ligeramente humillante.

⚔️ Leer antes de escribir
  1. Activa ScrollTrigger.defaults({ markers: true }) en un proyecto que ya tengas y recorre la página entera. Anota cada instancia cuyos marcadores no estén donde esperabas.
  2. Da un id y un indent escalonado a tres instancias que se solapen hasta que puedas leerlas todas.
  3. Provoca a propósito un intervalo de longitud negativa con un elemento bajo y arréglalo con un end relativo.
  4. Ejecuta la tabla de auditoría de la consola y busca instancias con trigger indefinido.
  5. Carga la página con la caché desactivada y una conexión lenta simulada, y comprueba si los marcadores acaban en un sitio distinto al de la carga rápida.