Scroll✨ Premium

Image Sequence

Une seule div dont 24 états (dégradé, arrondi, rotation, taille de 60 à 118 px) sont écrits dans un tableau JS et commutés d'après la position d'un conteneur à défilement de 280 px sur 920 px de course — un palier tous les 38 px, aucune image, 1,9 Ko de CSS et 3,5 Ko de JS. Les 12 premiers paliers sont presque invisibles sur le fond.

JavaScriptCSSSequence

Vous aurez accès à la démo interactive avec un compte gratuit.

Cet effet fait partie d'Effect.Labs — 811 effets vanilla, une partie gratuite, une partie premium. Scroll comprend 70 effets, dont 12 gratuits. Découvrir la catégorie →

3 exemples d'usage

Monter le module capteur — aide embarquée d'un objet connecté — Image Sequence exemple 1

① Monter le module capteur — aide embarquée d'un objet connecté

QuandÉcran d'aide d'un boîtier IoT au navigateur minimal (pas de WebGL, quelques Ko de stockage) : quatre étapes A → D, et une forme qui se transforme quand on fait défiler.
PourquoiZéro image, zéro WebGL : les 24 états sont un tableau, l'ensemble pèse 5,4 Ko de CSS et JS, rien n'est téléchargé ni stocké — là où fx-0795 (Pinned Image Sequence) charge un .glb dans <model-viewer> et exige WebGL. L'étape A/B/C/D se déduit de la même position (Math.floor(r × 4)) pour surligner la consigne en cours.
RéglagesCode vendu tel quel (ids, 24 états, conteneur 280 px, course 920 px) ; les quatre consignes lisent le même scrollTop ; indication traduite ; compteur et indication montés à 55 % de blanc (5,4:1) ; transition et pulsation coupées sous prefers-reduced-motion.
Trois finitions à faire défiler — configurateur d'un bureau — Image Sequence exemple 2

② Trois finitions à faire défiler — configurateur d'un bureau

QuandFiche produit de mobilier : le plateau change de finition — chêne naturel, gris anthracite, blanc mat — et son nom s'affiche ; prix et bouton ne bougent pas.
PourquoiChaque état est une entrée nommée du tableau, avec un index lisible : le nom de la finition, la pastille active, et en vrai la référence ou le prix en découlent. fx-0585 (Sticky Image Change) commute aussi quatre dégradés au défilement, mais en @keyframes sur animation-timeline: scroll() : aucun index, aucun nom, et rien dans Firefox.
RéglagesTrois états {bg, shape: 14px, rot: 0, size: 150} avec un champ name ; .imgseq-inner à 640 px pour un conteneur de 320 px (107 px par finition) ; compteur remplacé par le nom en role="status" ; ids suffixés -2.
k-means en trois étapes — figure d'un article de data science — Image Sequence exemple 3

③ k-means en trois étapes — figure d'un article de data science

QuandArticle technique en Markdown statique (GitHub Pages) : à droite, un nuage de points passe d'initialisation à assignation puis convergence pendant que le lecteur fait défiler.
PourquoiLe « film » tient dans trois chaînes background : des radial-gradient empilés dessinent 12 points et 3 centroïdes, recolorés puis regroupés — un tableau JS, sans image ni bibliothèque, collé dans la page sans étape de build. fx-0795 aurait demandé un runtime WebGL et un modèle à héberger.
RéglagesTrois états, size: 170, shape: 12px, rot: 0, bg = liste de 15 radial-gradient par état ; conteneur 300 px, .imgseq-inner 720 px ; légende de figure liée à l'index.

Comment ça marche

Le conteneur .scroll-container (100% × 280px, overflow: hidden auto, scroll-behavior: smooth, barre de 4 px stylée en ::-webkit-scrollbar seulement) contient .imgseq-inner, un bloc vide de 1200px : la course fait donc 1200 − 280 = 920 px. Dedans, .imgseq-display est en position: sticky; top: 0 avec une hauteur de 280px, celle du conteneur — la vitrine reste plein cadre du début à la fin. Elle porte le « film », .imgseq-frame, et le compteur .imgseq-counter (« Frame 1 / 24 », 10,4 px, blanc à 30 %). Le HTML vendu fixe déjà sur la frame, en attribut style, l'état 1 tel que le script l'avait laissé (dégradé #1a1a2e → #16213e, arrondi 20 px, 60 × 60) ; le script le réécrit au chargement, c'est inoffensif — mais les 120px du CSS ne s'appliquent jamais, pas plus que font-size: 1.5rem et font-weight: 800 sur une boîte sans texte. Le ::before est un anneau blanc à 10 % dont l'arrondi reste à 20 px quel que soit l'état, et .scroll-hint-label (« Scrollez pour explorer ») pulse de .3 à .7 d'opacité toutes les 2 s.

Le tableau frameStates contient 24 objets {bg, shape, rot, size}. À chaque événement scroll du conteneur, le script calcule r = scrollTop / (scrollHeight − clientHeight), puis i = min(floor(r × 24), 23), et écrit cinq styles en ligne : background, border-radius, transform: rotate(), width, height, plus le texte du compteur. Mesuré : les paliers tombent à 0, 39, 77, 115 … 882 px — 38,33 px chacun, le 24ᵉ tenant de 882 à 920. Le transition: .2s de la frame lisse la taille, l'arrondi et la rotation (60 → 118 px en 240 ms sur un saut de 0 à 700 px) mais pas le dégradé, qu'un navigateur n'interpole pas : il est déjà remplacé 61 ms après le saut. Avec une souris à crans (100 px par cran dans Chrome), un cran vaut 2,6 paliers : mesuré, la suite affichée est 1, 3, 6, 8, 11, 14, 16, 19, 21, 24 — 10 états sur 24 ; seuls un pavé tactile ou une roue libre passent par tous.

Les états eux-mêmes : douze dégradés sombres d'abord (du bleu nuit #1a1a2e → #16213e au prune #6a1a3c → #6e2168), dont le contraste avec le fond #0a0a0f mesure 1,20 à 1,83:1 — pendant les 460 premiers pixels, la moitié de la course, on voit un disque à peine plus clair que le noir grossir de 60 à 95 px et tourner de 0 à 165° ; à l'état 13 le dégradé saute à l'indigo #6366f1 → #7c3aed (3,84:1), puis violet, fuchsia, rose, orange, ambre, citron, cyan (12,24:1 à l'état 22) et retour à l'indigo. L'arrondi va de 20 à 10 px (états 1–7), passe au cercle (50%, 8–14), redescend de 45 à 25 % puis de 22 à 14 px ; la rotation avance de 15° par palier jusqu'à 345°. C'est un défilement imbriqué : mesuré avec 2000 px de page sous la zone, les dix premiers crans (920 px) sont absorbés par le conteneur avant que la page reprenne, et autant en remontant. L'écouteur scroll est posé sur le conteneur seul, jamais retiré — il disparaît avec l'élément ; pas de requestAnimationFrame, pas d'écouteur resize (la course est relue à chaque événement). Aucune console d'erreur, aucune variable CSS du site.

Accessibilité

  • prefers-reduced-motion absent du code : mesuré sous reduce, l'indication pulse toujours (pulseHint 2 s infini), la transition de .2 s reste active et les états commutent — dont une rotation de 270° en 200 ms quand un saut de molette traverse 18 paliers. Le changement piloté par le défilement est initié par l'utilisateur (toléré par WCAG 2.3.3), pas la pulsation ni le tour sur lui-même : ajoutez @media (prefers-reduced-motion: reduce) { .imgseq-frame { transition: none } .scroll-hint-label { animation: none } } et mettez rot à 0 dans le tableau.
  • Clavier : la zone est un div sans tabindex. Chromium rend pourtant les conteneurs défilants focalisables — mesuré : Tab arrive sur .scroll-container, chaque Flèche bas vaut 40 px, deux pressions affichent l'état 3 ; Firefox fait de même, Safari non. Posez tabindex="0" et un aria-label (ou role="region") pour que ce soit vrai partout ; Page suivante parcourt 245 px, soit six paliers d'un coup.
  • Lecteurs d'écran : le compteur est réécrit à chaque événement scroll sans aria-live, la frame est un div vide et l'anneau un ::before — rien n'est annoncé, rien n'est nommé. Mettez aria-hidden="true" sur .imgseq-frame et l'indication ; si l'état a un sens (nom d'une finition, numéro d'étape), annoncez-le dans une zone role="status" mise à jour seulement quand l'index change — le script, lui, écrit ses cinq styles et le texte même quand l'index n'a pas bougé.
  • Contraste et textes : compteur et indication en rgba(255,255,255,.3) sur #0a0a0f mesurent 2,62:1 en 10,4 px, sous les 4,5:1 — passez à .5 (5,36:1) ou .6 (7,30:1). Le compteur dit « Frame » (chaîne du JS), l'indication « Scrollez pour explorer » (HTML) : deux langues dans le code vendu. Enfin, la zone capte 920 px de molette avant de rendre la page — dix crans — sans autre signal que l'indication ; raccourcissez .imgseq-inner ou gardez la zone à l'écart du fil de lecture principal.

Compatibilité navigateur

JavaScript ES2015 (const, fonction fléchée, gabarit de chaîne). CSS : position: sticky, inset, overflow à deux valeurs, font-variant-numeric, scroll-behavior. La commutation repose sur un écouteur scroll classique : elle fonctionne dans Firefox, contrairement aux effets de la catégorie écrits en animation-timeline: scroll() (fx-0585, fx-0545…), que Firefox n'active dans aucune version. Zéro dépendance.

Chrome 87+✓ Complet
Firefox 66+✓ Complet (barre de défilement native, non stylée)
Safari 14.1+✓ Complet
Edge 88+✓ Complet
Mobile iOS 14.5+✓ Complet (défilement au doigt)
Android Chrome✓ Complet (défilement au doigt)

Sans JavaScript, la zone défile ses 920 px pour rien : la frame reste à l'état 1 écrit en attribut (60 px, bleu nuit) et le compteur affiche « Frame 1 / 24 » (mesuré). Sans inset (navigateurs d'avant 2020), seul l'anneau blanc à 10 % disparaît. Sans position: sticky, la vitrine défile avec le bloc de 1200 px et sort du cadre.

Le code

Structure HTML à coller dans votre page (CSS + JS disponibles avec un compte premium) :

index.html — structure
<div class="scroll-container" id="imgseq-scroll">
  <div class="imgseq-inner">
    <div class="imgseq-display">
      <div class="imgseq-frame" id="imgseq-frame"></div>
      <div class="imgseq-counter" id="imgseq-counter">Frame 1 / 24</div>
    </div>
  </div>
</div>
<span class="scroll-hint-label">Scrollez pour explorer</span>
🔒 Débloquer le code complet — à partir de 2,99 € le 1er mois

HTML + CSS + JS complet, prêt à coller — avec des centaines d'effets premium.

Personnaliser

Options passées à l'API ou attributs data-* :

Option / propriétéDéfautEffet
Tableau des états (JS — frameStates) 24 objets {bg, shape, rot, size} Ajoutez ou retirez des entrées : le compte suit (totalFrames = frameStates.length) et les paliers se recalculent (course ÷ n). bg accepte n'importe quelle valeur de background : liste de dégradés, couleur unie, url() d'une image préchargée.
Course (CSS — .imgseq-inner { height: 1200px } et .scroll-container { height: 280px }) 1200 − 280 = 920 px, soit 38 px par palier La différence des deux hauteurs est la distance à parcourir. À 600 px de bloc, la course tombe à 320 px et un cran de molette (100 px) saute sept paliers ; visez au moins 100 px par état si une souris à crans doit tous les montrer.
Rotation (JS — rot) 0 → 345° par pas de 15° Écrite en transform: rotate() et lissée par la transition : un saut de plusieurs paliers fait tourner la forme d'autant en 200 ms. Mettez 0 partout pour une photo de produit ou une figure.
Taille (JS — size) 60 → 118 (état 19) → 95 px Appliquée à width et height : la frame est toujours carrée. Pour un rectangle, ajoutez un champ et écrivez les deux dimensions séparément.
Transition (CSS — .imgseq-frame { transition: .2s }) .2s sur toutes les propriétés Lisse taille, arrondi et rotation ; le dégradé, lui, saute toujours (mesuré : remplacé en 61 ms). 0s pour un film à crans francs, .4s pour un morphing plus lent, none sous prefers-reduced-motion.
Palette (JS — bg) 12 dégradés sombres (#1a1a2e#6a1a3c) puis 12 vifs (#6366f1#22d3ee) La moitié sombre mesure 1,20 à 1,83:1 sur #0a0a0f, à peine visible. Commencez le tableau à l'état 13, ou éclaircissez les douze premiers ; sur fond clair, c'est l'inverse : les états citron et ambre faiblissent.
Compteur (JS — `Frame ${i+1} / ${totalFrames}` ; CSS — .imgseq-counter) « Frame n / 24 », 10,4 px, blanc à 30 % Réécrit à chaque événement. Remplacez le gabarit par un champ de l'état (t.name) pour afficher un nom ; montez l'opacité à .5 (5,36:1) pour passer le seuil de 4,5:1.
Indication (HTML — .scroll-hint-label ; CSS — pulseHint) « Scrollez pour explorer », pulsation 2 s infinie Texte en français codé dans le HTML, à traduire ou à retirer ; l'animation ne s'arrête jamais et n'est pas conditionnée à prefers-reduced-motion.

FAQ

Nulle part. Les 24 « images » sont des états CSS — un dégradé, un arrondi, une rotation, une taille — écrits dans frameStates et posés sur une seule div ; rien n'est chargé. Pour de vraies photos, donnez à chaque état bg: "url(frame-01.jpg) center / cover", gardez size constant et rot: 0, et préchargez les fichiers (new Image().src = … au chargement) : l'échange se fait au moment du défilement, une image pas encore arrivée laisse la frame vide, et la transition de .2 s n'interpole pas plus deux images que deux dégradés. La version avec un vrai objet en 3D est fx-0795 (Pinned Image Sequence) : un modèle .glb dans <model-viewer>, donc WebGL et un fichier à héberger. Celle-ci coûte 5,4 Ko et tourne sur n'importe quel navigateur qui exécute du JavaScript.
fx-0585 est en CSS pur : une @keyframes à quatre arrêts (0, 25, 50, 75 %) sur animation-timeline: scroll(), sans JavaScript — mais aussi sans rien à lire : pas d'index, pas de nom, pas d'événement, et aucune version de Firefox n'active animation-timeline, où le bloc reste sur son premier dégradé. fx-0583 passe par un écouteur scroll : il fonctionne dans Firefox 66+, et il expose i et l'objet d'état à chaque événement — de quoi afficher un nom, synchroniser une légende ou déclencher un chargement. Le prix : six écritures de style par événement au lieu d'une animation composée. Prenez fx-0585 pour un décor que rien d'autre ne doit suivre, fx-0583 dès qu'un état a un nom ou que d'autres éléments doivent le suivre.
Parce que les états 1 à 12 sont des dégradés presque noirs : mesuré au milieu du dégradé, 1,20:1 (état 1) à 1,83:1 (état 12) contre le fond #0a0a0f, sous les 3:1 attendus d'un élément graphique ; l'anneau ::before à 10 % de blanc fait 1,05:1 sur l'état 1. Pendant ces 460 premiers pixels — exactement la moitié des 920 px de course — seule la taille (60 → 95 px) et la rotation (0 → 165°) changent, sur une forme qu'on distingue à peine. À l'état 13 (dès 460 px), le dégradé saute à #6366f1 → #7c3aed (3,84:1) et la palette devient vive jusqu'à la fin. Deux remèdes : commencer le tableau à l'état 13 (douze paliers de 77 px, plus lisibles à la molette), ou remonter la luminosité des douze premiers dégradés.