Communitygithub.com

Niltra/web-scrollytelling

Implementa experiencias de scroll cinematográficas estilo Apple (hero pinneado, secuencia de frames en canvas, video scrubbing, capítulos narrados) con GSAP ScrollTrigger, sin scroll-jacking y con rendimiento medido. Úsala al construir heroes animados, secciones pinneadas, galerías horizontales o cualquier efecto "la web es como un vídeo que vas moviendo".

What is web-scrollytelling?

web-scrollytelling is a Claude Code agent skill that implementa experiencias de scroll cinematográficas estilo Apple (hero pinneado, secuencia de frames en canvas, video scrubbing, capítulos narrados) con GSAP ScrollTrigger, sin scroll-jacking y con rendimiento medido. Úsala al construir heroes animados, secciones pinneadas, galerías horizontales o cualquier efecto "la web es como un vídeo que vas moviendo".

Works with~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Niltra/agent-skills-web/tree/HEAD/skills/web-scrollytelling

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

Scrollytelling cinematográfico

Arquitectura base (no negociable)

  • Un solo pin, una sola línea de tiempo. El hero (o el capítulo estrella) pinnea un contenedor que dura N viewport-heights y un progreso 0→1 dirige todo. Si hay más de una animación de scroll compitiendo, es una web normal con efectos: no es esto.
  • Nunca scroll-jacking. El usuario controla la velocidad con su scroll nativo; la animación se "scrubea" (scrub: true). Prohibido secuestrar la rueda o mover el scroll por él.
  • Un solo bucle de animación. Lenis integrado en el ticker de GSAP (autoRaf: false) y ScrollTrigger sincronizado; o scroll nativo con scrub. Dos bucles compitiendo es la causa nº1 de jank y no se arregla optimizando assets.
  • El texto vive en DOM, superpuesto al canvas, con capas por progreso. Nunca renderizar copy dentro del canvas.

Secuencias de frames (técnica tipo Apple)

  • Fuente preferida: vídeo real → frames con ffmpeg (fps=12..24, escala a resolución de pantalla). Exportar WebP q≈68-75 y afinar mirando el resultado, no por intuición.
  • Máximo ~90-120 frames; en móvil servir la mitad o versión 720p.
  • Carga con ventana deslizante: mantener ±6-10 frames alrededor del actual; jamás decodificar la secuencia completa (una secuencia 1080p completa mata iOS Safari).
  • Precarga con IntersectionObserver (rootMargin ~800px), requestIdleCallback para decode y fetchPriority="low" salvo el hero.
  • Canvas con devicePixelRatio capado a 1.5 y cover/contain calculado; dibujar solo frames decodificados; si falta el frame exacto, usar el más cercano ya cargado (nunca pintar en negro).
  • Caché inmutable (Cache-Control: public, max-age=31536000, immutable) con nombre de archivo versionado.
  • Fallback con prefers-reduced-motion y conexión lenta: póster estático + capítulos normales sin pin.
  • Alternativa más simple cuando no hace falta post-proceso: intercambiar src de una <img> por frame (fiable en todos los navegadores, sin canvas).

Vídeo scrubbing (si se usa <video>)

  • currentTime dirigido por progreso en un único requestAnimationFrame, con suavizado; coalescer seeks (Safari acumula cola).
  • Cargar como blob para que sea 100% seekable; encode con GOP corto (keyframe cada pocos frames) o el scrub tartamudea.
  • Nunca autoplay: lo mueve el scroll.

Capítulos y overlays

  • Beats definidos por rangos de progreso (10%, 35%, 60%, 85%...), con fade in/out y entrada alternando lados para ritmo.
  • Cada capítulo mantiene su texto en el DOM en orden semántico correcto para SEO.
  • Galerías horizontales: pin de la sección + x ligado al progreso; las cards se escalan hacia el centro.

Rendimiento y verificación

  • Animar solo transform, opacity y filter; will-change puntual y retirado al terminar.
  • Medir frames con requestAnimationFrame en local y en móvil real si es posible; objetivo 60fps en portátil medio.
  • La captura headless puede leer el canvas negro (buffer no preservado): verificar por DOM (estilos computados, elementos, estado expuesto en window.debug) y con el modo reducido activado.
  • Probar a 375/768/1280/1440; el redimensionado de un navegador de escritorio no reproduce el crash de memoria de iOS: decirlo en el informe de QA.

Estructura de datos recomendada

Un array ordenado de "planos": cada plano es { tipo: 'frames' | 'overlay' | 'galeria', ... }. Reordenar la película = reordenar la lista. Los frames se referencian por patrón (/seq/plano-01/0001.webp).

Individual skills in this repo

This repo contains 3 individual skills — each has its own dedicated page.

Related Skills