Communitygithub.com

KILN-skills/scroll-animation

Construye animaciones basadas en scroll con GSAP, ScrollTrigger, SplitText y Lenis smooth scroll — revelaciones, secciones fijas, scroll horizontal, paralaje, timelines con scrub, efectos de texto — en vanilla JS, React, Next.js, Vue/Nuxt o Astro, con configuración correcta, limpieza y alternativas responsivas.

¿Qué es scroll-animation?

Este skill especializado te guía en la creación de animaciones de scroll avanzadas y de alto rendimiento web, combinando GSAP, ScrollTrigger, SplitText y Lenis. Cubre la configuración completa desde la instalación, pasando por la integración específica en frameworks modernos como React, Next.js, Vue/Nuxt y Astro, hasta las mejores prácticas que evitan los errores más comunes. Incluye reglas críticas para usar ScrollTrigger correctamente (como no anidar triggers, usar `ease: 'none'` con `scrub` y priorizar el orden de creación), manejo responsivo con `gsap.matchMedia()`, soporte para `prefers-reduced-motion`, y técnicas de optimización de rendimiento. Es indispensable cuando el usuario menciona cualquier efecto relacionado con scroll como paralaje, secciones fijas (pin), scrub, scroll horizontal o narración visual con scroll, asegurando que la implementación sea robusta, accesible y funcione en múltiples entornos.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/KILN-skills/kiln/tree/HEAD/skills/scroll-animation

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Animación de Scroll (GSAP + ScrollTrigger + Lenis)

Si los skills oficiales de GreenSock (gsap-core, gsap-scrolltrigger, gsap-react, gsap-plugins) están instalados, úsalos para los detalles de la API; este skill cubre la configuración del proyecto, la integración y los errores que rompen sitios reales.

1. Instalación

npm i gsap lenis
npm i @gsap/react   # Solo React / Next.js

Todos los plugins de GSAP (ScrollTrigger, SplitText, Flip, ScrollSmoother, MorphSVG, DrawSVG…) se incluyen en el paquete público gsap y son gratuitos para uso comercial. Impórtalos desde gsap/<NombrePlugin> y llama a gsap.registerPlugin(...) una vez.

2. Módulo de configuración único

Crea lib/motion/setup.(js|ts) (o animations/) que se importe una vez en el cliente:

import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import { SplitText } from "gsap/SplitText";
import Lenis from "lenis";
import "lenis/dist/lenis.css";

gsap.registerPlugin(ScrollTrigger, SplitText);

export function initSmoothScroll() {
  if (matchMedia("(prefers-reduced-motion: reduce)").matches) return null;
  const lenis = new Lenis();
  lenis.on("scroll", ScrollTrigger.update);
  const tick = (time) => lenis.raf(time * 1000); // GSAP usa segundos, Lenis prefiere ms
  gsap.ticker.add(tick);
  gsap.ticker.lagSmoothing(0);
  return () => { gsap.ticker.remove(tick); lenis.destroy(); };
}

Usa o Lenis o GSAP ScrollSmoother, no ambos.

3. Integración con frameworks

React / Next.js (App Router) — los archivos que usen GSAP necesitan "use client". Siempre usa useGSAP (limpieza automática, sin doble ejecución en Strict Mode):

"use client";
import { useRef } from "react";
import gsap from "gsap";
import { useGSAP } from "@gsap/react";
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(useGSAP, ScrollTrigger);

export function Features() {
  const root = useRef<HTMLElement>(null);
  useGSAP(() => {
    const mm = gsap.matchMedia();
    mm.add("(prefers-reduced-motion: no-preference)", () => {
      gsap.from(".feature", {
        autoAlpha: 0, y: 32, stagger: 0.08, ease: "power3.out",
        scrollTrigger: { trigger: root.current, start: "top 75%" },
      });
    });
  }, { scope: root }); // Los selectores están limitados a este componente
  return <section ref={root}>…</section>;
}
  • Manejadores de eventos que crean animaciones: envuélvelos con contextSafe de useGSAP.
  • Proveedor de smooth scroll: un componente cliente en el layout raíz que llame a initSmoothScroll() en un efecto (o ReactLenis de lenis/react con autoRaf: false y el ticker de GSAP).
  • Cambio de ruta en Next.js: scroll al inicio (lenis.scrollTo(0, { immediate: true })) y ScrollTrigger.refresh() después de que monte la nueva página.

Vue / Nuxt — crea en onMounted dentro de gsap.context(() => {…}, rootEl), llama a ctx.revert() en onUnmounted. Nuxt: plugin solo cliente (plugins/gsap.client.ts).

Astro / multi-página estática — un <script> en el layout; con View Transitions, reinicia en astro:page-load y mata los triggers en astro:before-swap.

Vanilla — un main.js que se ejecute después de que el DOM esté listo; agrupa por sección en gsap.context().

4. Reglas de ScrollTrigger que previenen el 90% de los errores

  • Pon scrollTrigger en una timeline o tween de nivel superior — nunca en tweens anidados dentro de una timeline.
  • Usa ease: "none" con scrub.
  • El orden de creación importa: crea los triggers de arriba a abajo en el orden de la página (o establece refreshPriority), especialmente con pinning.
  • Fija un contenedor, no el elemento que animas. No fijes dentro de un padre con transform. Evita pinSpacing: false a menos que sea intencional.
  • Usa valores de función (x: () => …) + invalidateOnRefresh: true para cualquier cosa que dependa del tamaño de la ventana.
  • Llama a ScrollTrigger.refresh() después de que imágenes, fuentes o contenido dinámico cambien el layout (window.addEventListener("load", …), document.fonts.ready).
  • Depura con markers: true — elimínalos antes de publicar.
  • Estados ocultos iniciales: usa gsap.from/fromTo o autoAlpha establecidos por JS, para que el contenido sea visible sin JS.
  • once: true para revelaciones que no deben reproducirse de nuevo (la mayoría).

5. Responsive y movimiento reducido

Envuelve cada escena en gsap.matchMedia():

const mm = gsap.matchMedia();
mm.add({
  isDesktop: "(min-width: 1024px)",
  isMobile: "(max-width: 1023px)",
  motionOK: "(prefers-reduced-motion: no-preference)",
}, ({ conditions: c }) => {
  if (!c.motionOK) return;            // layout estático, sin animación
  if (c.isDesktop) { /* escenas fijas / horizontales */ }
  if (c.isMobile)  { /* solo apariciones simples */ }
});

matchMedia revierte automáticamente cuando las condiciones cambian — sin limpieza manual.

6. Rendimiento

  • Anima x/y/scale/rotation/autoAlpha (transformaciones + opacidad). Evita animar width/height/top/left y filter: blur() pesado en áreas grandes.
  • Prefiere una timeline por sección en lugar de docenas de triggers separados; usa ScrollTrigger.batch para muchas revelaciones idénticas.
  • gsap.quickTo para efectos de seguimiento del puntero.
  • Imágenes grandes dentro de escenas con scrub: usa sizes adecuados y evita escalar mapas de bits enormes en cada fotograma.

7. Verificación

Desplázate por toda la página arriba y abajo rápido; cambia el tamaño de la ventana; recarga a mitad de página; navega a otra página y regresa (SPA) — sin saltos, sin pines duplicados, sin errores en consola. Prueba con movimiento reducido activado, a 375px y con limitación de CPU.

Recetas para efectos comunes: ../motion-direction/references/patterns.md.

Skills relacionados