Communitygithub.com

Niltra/web-scrollytelling

Implementa experiências de scroll cinematográficas estilo Apple (hero fixo, sequência de frames em canvas, video scrubbing, capítulos narrados) com GSAP ScrollTrigger, sem scroll-jacking e com desempenho medido. Use ao construir heróis animados, seções fixas, galerias horizontais ou qualquer efeito 'a web é como um vídeo que você vai movendo'.

O que é web-scrollytelling?

Esta skill capacita agentes de IA a construir experiências de scroll narrativo de alto nível, inspiradas em campanhas da Apple, utilizando GSAP ScrollTrigger e a arquitetura de 'um único pin, uma única linha do tempo'. Ela resolve diretamente problemas comuns como jank, memory leaks em dispositivos móveis e scroll-jacking. A skill suporta três técnicas principais de animação: sequências de frames em canvas (com carregamento eficiente por janela deslizante), vídeo scrubbing (com GOP curto e carregamento como blob) e overlays de capítulos com fade in/out baseados em progresso. É projetada para plataformas como Cursor, Claude Code e Codex, e inclui regras rigorosas de desempenho: animar apenas transform, opacity e filter; medir fps com requestAnimationFrame; e verificar estados pelo DOM, nunca pelo canvas headless. O texto dos capítulos vive no DOM para SEO, e toda a experiência é controlada pelo scroll nativo do usuário, sem sequestro. A estrutura de dados recomendada é um array ordenado de 'planos', facilitando a reordenação da narrativa.

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

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Scrollytelling cinematográfico

Arquitetura base (não negociável)

  • Um único pin, uma única linha do tempo. O hero (ou o capítulo principal) fixa um contêiner que dura N viewport-heights e um progresso 0→1 dirige tudo. Se houver mais de uma animação de scroll competindo, é uma web normal com efeitos: não é isso.
  • Nunca scroll-jacking. O usuário controla a velocidade com seu scroll nativo; a animação é "scrubeada" (scrub: true). Proibido sequestrar a roda ou mover o scroll pelo usuário.
  • Um único loop de animação. Lenis integrado no ticker do GSAP (autoRaf: false) e ScrollTrigger sincronizado; ou scroll nativo com scrub. Dois loops competindo é a causa nº1 de jank e não se resolve otimizando assets.
  • O texto vive no DOM, sobreposto ao canvas, com camadas por progresso. Nunca renderizar cópia dentro do canvas.

Sequências de frames (técnica tipo Apple)

  • Fonte preferida: vídeo real → frames com ffmpeg (fps=12..24, escala para resolução de tela). Exportar WebP q≈68-75 e ajustar olhando o resultado, não por intuição.
  • Máximo ~90-120 frames; em dispositivos móveis servir metade ou versão 720p.
  • Carregar com janela deslizante: manter ±6-10 frames ao redor do atual; jamais decodificar a sequência completa (uma sequência 1080p completa mata iOS Safari).
  • Pré-carregar com IntersectionObserver (rootMargin ~800px), requestIdleCallback para decode e fetchPriority="low" exceto para o hero.
  • Canvas com devicePixelRatio limitado a 1.5 e cover/contain calculado; desenhar apenas frames decodificados; se faltar o frame exato, usar o mais próximo já carregado (nunca pintar em preto).
  • Cache imutável (Cache-Control: public, max-age=31536000, immutable) com nome de arquivo versionado.
  • Fallback com prefers-reduced-motion e conexão lenta: pôster estático + capítulos normais sem pin.
  • Alternativa mais simples quando não é necessário pós-processo: trocar src de uma <img> por frame (confiável em todos os navegadores, sem canvas).

Vídeo scrubbing (se usar <video>)

  • currentTime dirigido por progresso em um único requestAnimationFrame, com suavização; coalescer seeks (Safari acumula fila).
  • Carregar como blob para ser 100% seekable; codificar com GOP curto (keyframe a cada poucos frames) ou o scrub trava.
  • Nunca autoplay: quem move é o scroll.

Capítulos e overlays

  • Beats definidos por intervalos de progresso (10%, 35%, 60%, 85%...), com fade in/out e entrada alternando lados para ritmo.
  • Cada capítulo mantém seu texto no DOM em ordem semântica correta para SEO.
  • Galerias horizontais: pin da seção + x ligado ao progresso; os cards escalonam em direção ao centro.

Desempenho e verificação

  • Animar apenas transform, opacity e filter; will-change pontual e removido ao terminar.
  • Medir frames com requestAnimationFrame localmente e em dispositivo móvel real, se possível; objetivo 60fps em notebook médio.
  • A captura headless pode ler o canvas preto (buffer não preservado): verificar pelo DOM (estilos computados, elementos, estado exposto em window.debug) e com o modo reduzido ativado.
  • Testar em 375/768/1280/1440; o redimensionamento de um navegador desktop não reproduz o crash de memória do iOS: informar no relatório de QA.

Estrutura de dados recomendada

Um array ordenado de "planos": cada plano é { tipo: 'frames' | 'overlay' | 'galeria', ... }. Reordenar o filme = reordenar a lista. Os frames são referenciados por padrão (/seq/plano-01/0001.webp).

Individual skills in this repo

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

Habilidades Relacionadas