Communitygithub.com

Niltra/web-scrollytelling

Implémentez des expériences de défilement cinématographiques façon Apple (hero épinglé, séquence d'images sur canvas, vidéo scrubbing, chapitres narrés) avec GSAP ScrollTrigger, sans scroll-jacking et avec des performances mesurées. Utilisez-le pour créer des héros animés, des sections épinglées, des galeries horizontales ou tout effet où « le web est comme une vidéo que l'on fait défiler ».

Qu'est-ce que web-scrollytelling ?

Cette compétence fournit une architecture et des techniques éprouvées pour créer des effets de scroll avancés et fluides, inspirés des sites d'Apple. Elle se concentre sur un seul point d'ancrage (pin) et une seule timeline, évitant les conflits d'animations. Les séquences d'images sont chargées avec une fenêtre glissante pour économiser la mémoire, et le scroll natif est préservé (pas de scroll-jacking). Des astuces de performance sont incluses (limitation du `devicePixelRatio`, animation des seules propriétés `transform`/`opacity`/`filter`). La compétence prend en charge aussi bien le canvas que la balise `<video>`, et gère les chapitres avec des transitions DOM. Elle est conçue pour GSAP ScrollTrigger et s'intègre avec Lenis si désiré.

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

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Scrollytelling cinématographique

Architecture de base (non négociable)

  • Un seul pin, une seule timeline. Le hero (ou le chapitre vedette) épingle un conteneur qui dure N viewport-heights et une progression 0→1 dirige tout. S'il y a plus d'une animation de scroll en compétition, c'est un site web normal avec des effets : ce n'est pas ça.
  • Jamais de scroll-jacking. L'utilisateur contrôle la vitesse avec son scroll natif ; l'animation se "scrube" (scrub: true). Il est interdit de détourner la molette ou de déplacer le scroll à sa place.
  • Une seule boucle d'animation. Lenis intégré dans le ticker de GSAP (autoRaf: false) et ScrollTrigger synchronisé ; ou scroll natif avec scrub. Deux boucles en compétition sont la cause n°1 de jank et ne se corrigent pas en optimisant les assets.
  • Le texte vit dans le DOM, superposé au canvas, avec des calques par progression. Ne jamais rendre de copie à l'intérieur du canvas.

Séquence d'images (technique type Apple)

  • Source préférée : vidéo réelle → images avec ffmpeg (fps=12..24, échelle à la résolution d'écran). Exporter en WebP q≈68-75 et affiner en regardant le résultat, pas par intuition.
  • Maximum ~90-120 images ; sur mobile servir la moitié ou une version 720p.
  • Chargement avec fenêtre glissante : garder ±6-10 images autour de l'actuelle ; ne jamais décoder la séquence complète (une séquence 1080p complète tue iOS Safari).
  • Préchargement avec IntersectionObserver (rootMargin ~800px), requestIdleCallback pour le décodage et fetchPriority="low" sauf pour le hero.
  • Canvas avec devicePixelRatio plafonné à 1.5 et cover/contain calculé ; ne dessiner que les images décodées ; si l'image exacte manque, utiliser la plus proche déjà chargée (ne jamais peindre en noir).
  • Cache immuable (Cache-Control: public, max-age=31536000, immutable) avec nom de fichier versionné.
  • Fallback avec prefers-reduced-motion et connexion lente : poster statique + chapitres normaux sans pin.
  • Alternative plus simple quand aucun post-traitement n'est nécessaire : échanger la src d'une <img> par image (fiable dans tous les navigateurs, sans canvas).

Vidéo scrubbing (si utilisation de <video>)

  • currentTime piloté par la progression dans une seule requestAnimationFrame, avec lissage ; coalescer les seeks (Safari accumule une file d'attente).
  • Charger en tant que blob pour qu'elle soit 100% seekable ; encoder avec un GOP court (keyframe tous les quelques frames) sinon le scrub hoquette.
  • Jamais d'autoplay : c'est le scroll qui la déplace.

Chapitres et overlays

  • Beats définis par des plages de progression (10%, 35%, 60%, 85%...), avec fade in/out et entrée alternant les côtés pour le rythme.
  • Chaque chapitre conserve son texte dans le DOM dans un ordre sémantique correct pour le SEO.
  • Galeries horizontales : pin de la section + x lié à la progression ; les cartes se mettent à l'échelle vers le centre.

Performance et vérification

  • Animer uniquement transform, opacity et filter ; will-change ponctuel et retiré à la fin.
  • Mesurer les images avec requestAnimationFrame en local et sur mobile réel si possible ; objectif 60fps sur un ordinateur portable moyen.
  • La capture headless peut lire le canvas noir (buffer non préservé) : vérifier par le DOM (styles calculés, éléments, état exposé dans window.debug) et avec le mode réduit activé.
  • Tester à 375/768/1280/1440 ; le redimensionnement d'un navigateur de bureau ne reproduit pas le crash mémoire d'iOS : le mentionner dans le rapport QA.

Structure de données recommandée

Un tableau ordonné de "plans" : chaque plan est { tipo: 'frames' | 'overlay' | 'galeria', ... }. Réorganiser le film = réorganiser la liste. Les images sont référencées par motif (/seq/plano-01/0001.webp).

Individual skills in this repo

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

Skills associés