Scroll✨ Premium

Path Drawing on Scroll

Une courbe SVG de 364 unités qui se trace au défilement — stroke-dashoffset 600 → 0 sur une timeline nommée, 1 200 px de course — et quatre jalons qui grossissent (r 0 → 5 px) chacun sur sa propre plage animation-range : 0-15 %, 25-40 %, 50-65 %, 75-90 %. CSS seul, zéro JavaScript.

CSSSVGscroll()

Mis à jour le

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

Trajet d'un colis entre quatre plateformes — page de suivi de livraison — Path Drawing on Scroll exemple 1

① Trajet d'un colis entre quatre plateformes — page de suivi de livraison

QuandPage de suivi d'une commande : la ligne se trace de l'entrepôt au domicile pendant que le client descend, et chaque plateforme (départ, hub régional, agence, livraison) s'allume à son tour sous le texte de l'étape.
PourquoiChaque jalon a sa propre plage de défilement : la plateforme apparaît exactement quand son paragraphe arrive, sans une ligne de script — fx-0582 Timeline Scroll aligne des cartes sur un axe droit, pas un trajet courbe avec des points datés.
RéglagesMoteur vendu intact (timeline --pathScroll, tiret 600 → 0, plages 0-15 / 25-40 / 50-65 / 75-90 %). Conteneur sous un en-tête de 48 px (312 px), plateau de 1 200 px en grille — carte collante de 230 px, colonne d'étapes — soit 888 px de course ; tracé propre de 302 unités avec pathLength="600" pour que la ligne finisse à 100 % ; jalons posés sur la courbe par getPointAtLength à 0, 37, 63 et 100 % de sa longueur ; dégradé userSpaceOnUse bleu → vert ; keyframes propres r 0 → 5.5px, liseré blanc ; les quatre cartes d'étape sont accrochées à la même timeline (opacity .32 → 1 sur les mêmes plages).
Courbe de progression d'un cours de guitare — plateforme de cours en ligne — Path Drawing on Scroll exemple 2

② Courbe de progression d'un cours de guitare — plateforme de cours en ligne

QuandPage du parcours d'un cours en ligne : la courbe monte des premiers accords aux solos, et quatre paliers (accords ouverts, rythmique, barrés, improvisation) se marquent quand l'élève fait défiler le programme.
PourquoiLa courbe qui se dessine est la progression elle-même, et les plages 0-15 % / 25-40 % / 50-65 % / 75-90 % se réajustent dans le CSS si un palier est ajouté — fx-0781 dessine des anneaux concentriques, pas une trajectoire.
RéglagesEn-tête 50 px, conteneur 310 px, plateau 1 240 px (course 930) en grille inversée : étapes à gauche, graphe collant de 240 px à droite ; tracé en cubiques de 256 unités avec pathLength="600" ; jalons à 0, 37, 63 et 100 % de la longueur ; axes et lignes de niveau pointillées dans le SVG ; dégradé userSpaceOnUse #38bdf8 → #a78bfa → #f472b6 ; jalons r 0 → 6px ; paliers de texte en opacity sur les plages du code.
Route de migration d'une espèce d'oiseaux — page pédagogique d'une réserve naturelle — Path Drawing on Scroll exemple 3

③ Route de migration d'une espèce d'oiseaux — page pédagogique d'une réserve naturelle

QuandFiche « La migration du milan noir » : la route se trace du nord au sud sur une carte stylisée, et quatre haltes (marais, estuaire, col, delta) surgissent chacune à son tour au fil du texte.
PourquoiLe tracé courbe et les haltes ponctuelles sont exactement le vocabulaire d'une migration : entre deux plages (15-25, 40-50, 65-75 %) seule la ligne avance, comme le vol entre deux haltes, et un jalon atteint reste allumé grâce à animation-fill-mode: both — fx-0796 ne trace rien, il déplace des panneaux.
RéglagesCarte en portrait (viewBox 0 0 200 250) dans une colonne collante de 210 px, en-tête 46 px, conteneur 314 px, plateau 1 260 px (course 946) ; fond de carte = un polygone de côte et trois parallèles pointillés dans le SVG ; tracé nord → sud de 240 unités avec pathLength="600", dégradé vertical #166534 → #65a30d → #b45309, jalons r 0 → 5.5px à liseré crème ; quatre haltes datées accrochées à --pathScroll.

Comment ça marche

Cinq boîtes : le cadre .demo-preview (flex centré, min-height: 300px, position: relative, fond #0a0a0f), le bloc défilant .pathdraw-container (width:100%;height:300px en style en ligne, overflow: hidden auto) qui déclare scroll-timeline: --pathScroll y, le plateau .pathdraw-spacer de 500 % (1 500 px, padding-top: 10px), le collant .pathdraw-sticky (top: 10px, width: 90%, max-width: 360px) et le svg.pathdraw-svg en viewBox 0 0 260 200, width: 100%; height: auto — 360 × 277 px dès que le cadre dépasse 400 px de large, 337 × 259 px à 390 px. Dedans : un linearGradient#pathGradient à trois arrêts (#6366f1, #d946ef, #f59e0b), le tracé M 20 180 Q 60 20, 130 100 T 240 30 (mesuré getTotalLength() = 364,42 unités, trait de 3, bouts ronds) et quatre circle avec cx/cy mais sans r. Course : 1 500 − 300 = 1 200 px. Le tracé et les cercles prennent animation-timeline: --pathScroll, écrit en propriétés longues (animation-duration: auto, linear, fill-mode: both) parce que le CSS a été relevé depuis les styles calculés. Sous le cadre, l'indice .slr-scroll-hint « Scrollez pour dessiner » (absolu, 8 px du bas, 11,2 px, blanc à 40 %) rebondit de 4 px toutes les 2 s. HTML 1,2 Ko, CSS 2,4 Ko, champ JS vide, aucune variable du site, aucune erreur console.

La ligne : stroke-dasharray: 600, stroke-dashoffset: 600, et @keyframes drawPath ramène l'offset à 0, linéairement sur toute la course — 0,5 unité par pixel défilé. Mesuré par crans de 150 px : 525, 450, 375, 300, 225, 150, 75, 0. La longueur visible vaut 600 − offset ; le tracé ne faisant que 364,42 unités, il est complet à l'offset 235,6, soit 60,7 % de la course (728 px) — les 39 % restants ne changent plus la ligne. Le dégradé est posé sur la boîte englobante (diagonale 0,0 → 100 %,100 %), pas le long du stylo : mesuré en fin de course, départ et arrivée sont magenta rgb(217, 70, 239), le sommet violet rgb(178, 81, 240), le creux rose rgb(226, 97, 169) — ni le bleu ni l'ambre des arrêts n'apparaissent sur la ligne.

Les jalons : @keyframes dotAppear fait passer r de 0 à 5px et fill de #6366f1 à #d946ef, et chaque .pathdraw-dot:nth-of-type(n) reçoit sa plage animation-range — 15 % de course, 180 px par jalon. Mesuré à mi-plage (7,5 / 32,5 / 57,5 / 82,5 %) : r 2,5 px, fill rgb(158, 86, 240). Rapporté au stylo : le jalon 1 est au départ, le stylo passe à l'abscisse du jalon 2 à 23,2 % (allumé de 25 à 40 %), à celle du jalon 3 à 38,3 % (50-65 %) et arrive au bout à 60,7 % quand le jalon 4 n'apparaît qu'entre 75 et 90 % — la ligne devance les jalons, de plus en plus, et les 10 % de course après le dernier ne changent rien. Les jalons 2 et 3 ne sont pas sur la courbe : 14 et 17 unités au-dessus (points les plus proches (92,5 ; 73,9) et (165 ; 126,2)). Deux copies sur une page : timelines indépendantes (mesuré 600 et 300 px d'offset) ; le doublon id="pathGradient" est sans effet tant que les deux dégradés sont identiques.

Accessibilité

  • prefers-reduced-motion absent du code : mesuré sous la préférence, le rebond de l'indice tourne (hintBounce running) et le tracé suit le défilement comme avant. Rien n'est caché : le dessin ne bouge que sous le geste du lecteur (WCAG 2.3.3, animation déclenchée par l'interaction). Ajoutez @media (prefers-reduced-motion: reduce) { .slr-scroll-hint { animation: none } } ; pour livrer le tracé fini sans défiler, reprenez sous cette media query les cinq règles du bloc @supports not.
  • Clavier : mesuré dans Chromium, Tab donne le focus au conteneur sans tabindex (anneau outline: auto 1px), flèche bas avance de 40 px (3,3 % de course, 20 unités de tiret), PageDown de 302 px, Fin va au bout — le tracé suit chaque touche. Safari et Firefox ne focalisent pas un bloc défilant sans tabindex="0" : ajoutez-le avec role="region", un aria-label et un :focus-visible. Au doigt, défilement imbriqué natif : le geste doit commencer dans le bloc, et overscroll-behavior: contain évite d'entraîner la page en bout de course.
  • Contraste : l'indice de 11,2 px en blanc à 40 % vaut 3,77:1 sur #0a0a0f (seuil 4,5:1), en français en dur, absolu par rapport au cadre — posez aria-hidden="true" dessus ou remplacez-le par une consigne lisible. Tracé et jalons sont des composants graphiques (seuil 3:1) : #6366f1 4,42:1, #d946ef 5,71:1, #f59e0b 9,2:1 sur le fond du cadre ; sans ce fond, sur blanc : 4,47 / 3,46 / 2,15:1, l'ambre passe sous le seuil. Un jalon de 5 unités fait 6,9 px de rayon à 360 px de large.
  • Lecteurs d'écran : le SVG n'a ni role ni <title>, rien n'est annoncé. Décoratif : aria-hidden="true". Porteur d'information : role="img" et aria-label, et surtout les étapes en texte dans le DOM, à côté — les cercles ne contiennent aucun texte et n'existent visuellement qu'après défilement. Là où la timeline manque, le repli @supports affiche le tracé fini et les quatre jalons sans défiler. Aucun JavaScript : rien à désactiver.

Compatibilité navigateur

CSS seul, 2,4 Ko : scroll-timeline nommée sur le conteneur, animation-timeline, animation-range par jalon, propriété CSS r animée, stroke-dasharray/stroke-dashoffset, position: sticky, overflow: hidden auto. Les trois premières fixent le seuil : Chrome et Edge 115+, Safari et iOS 26 (mesuré dans WebKit 26.5 : rendu identique à Chromium, offset 525 px et r 4,17 px après 150 px). Firefox : aucune version n'exécute animation-timeline (mesuré dans Firefox 153 : CSS.supports('animation-timeline: scroll()') = false). Zéro dépendance, zéro JavaScript.

Chrome 115+✓ Complet
Firefox✓ Aucune version — repli : tracé complet et quatre jalons posés, sans défilement (mesuré dans Firefox 153)
Safari 26+✓ Complet (avant : repli à plat)
Edge 115+✓ Complet
Mobile iOS 26+✓ Complet (défilement au doigt dans le bloc ; avant : repli à plat)
Android Chrome 115+✓ Complet

Le bloc @supports not (animation-timeline: scroll()) prend la main partout où la timeline manque (Firefox, Safari et iOS avant 26, Chrome avant 115) : animation: none sur le tracé et les jalons, stroke-dashoffset: 0, r: 5px et fill: #d946ef sur les quatre cercles, height: auto sur le plateau, display: none sur l'indice. Mesuré dans Firefox 153 : ligne complète, quatre jalons de 13,9 px, scrollHeight 300 = clientHeight (rien à faire défiler), indice masqué — le visiteur voit le dessin fini, à plat, sans une consigne « Scrollez pour dessiner » qui ne dessinerait rien. Sans ce bloc, Firefox arrivait au même offset 0 par une animation de 0 s, mais avec 1 200 px de course morte et l'indice qui rebondit. L'unité de r: 5px n'est pas décorative : CSS.supports('r: 5') vaut false dans WebKit et Firefox — seul Chrome accepte la valeur nue — et sans elle les jalons restent à r: 0, invisibles (mesuré dans WebKit 26.5 : ligne tracée, aucun jalon).

Le code

Structure HTML à coller dans votre page (le CSS + JS sont inclus avec Premium) :

index.html — structure
<div class="demo-preview">
  <div class="pathdraw-container">
    <div class="pathdraw-spacer">
      <div class="pathdraw-sticky">
        <svg class="pathdraw-svg" viewBox="0 0 260 200">
          <defs>
            <linearGradient id="pathGradient">…</linearGradient>
          </defs>
          <path class="pathdraw-path" d="…"></path>
          <circle class="pathdraw-dot" cx="…" cy="…"></circle>
          <circle class="pathdraw-dot" cx="…" cy="…"></circle>
          <circle class="pathdraw-dot" cx="…" cy="…"></circle>
          <circle class="pathdraw-dot" cx="…" cy="…"></circle>
        </svg>
      </div>
    </div>
  </div>
  <span class="slr-scroll-hint">…</span>
</div>
Débloquer le code complet — 9,90 € HT/mois

Le code complet HTML + CSS + JS de Path Drawing on Scroll, prêt à coller — et les 811 effets. Ou 299 € une fois, accès à vie.

Personnaliser

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

Option / propriétéDéfautEffet
Tracé (HTML — attribut d de .pathdraw-path) M 20 180 Q 60 20, 130 100 T 240 30 (364 unités) N'importe quel chemin SVG. Au-delà de 600 unités, la ligne ne finit jamais : montez stroke-dasharray, stroke-dashoffset et le 600 de drawPath, ou posez pathLength="600" sur le tracé (mesuré dans Chromium, WebKit et Firefox : le tiret est alors compté sur 600 quelle que soit la longueur réelle).
Longueur de tiret (CSS — stroke-dasharray: 600, stroke-dashoffset: 600, @keyframes drawPath) 600 → ligne complète à 60,7 % de la course La ligne finit quand 600 − offset atteint la longueur du tracé. Avec 365 aux trois endroits (ou pathLength="600"), le stylo arrive au bout à 100 % et passe les abscisses des jalons 2 et 3 à 38 et 63 % au lieu de 23 et 38.
Course (HTML — height:300px en ligne sur le conteneur ; CSS — .pathdraw-spacer { height: 500% }) 1 200 px (0,5 unité par pixel) Course = plateau − conteneur. 300% donne 600 px, deux fois plus vif. Le plateau est aussi le bloc conteneur du collant : avec 277 px de SVG et top: 10px, le dessin reste fixé jusqu'à 1 203 px, donc sur toute la course livrée.
Plages des jalons (CSS — .pathdraw-dot:nth-of-type(n) { animation-range }) 0% 15% · 25% 40% · 50% 65% · 75% 90% Chaque plage vaut 180 px de course ; 10 % restent inertes après le dernier jalon. Resserrez (75% 80%) pour un allumage sec, ou 60% 75% sur le quatrième pour qu'il coïncide avec l'arrivée du stylo à 60,7 %.
Jalons (HTML — cx/cy ; CSS — @keyframes dotAppear) r 0 → 5px, fill #6366f1 → #d946ef 5 unités = 6,9 px de rayon à 360 px. Gardez l'unité sur r : la valeur nue n'est acceptée que par Chrome. Les jalons 2 et 3 livrés sont 14 et 17 unités au-dessus de la courbe ; path.getPointAtLength(l) dans la console donne le point exact à poser en cx/cy.
Dégradé (SVG — #pathGradient, trois arrêts) #6366f1 → #d946ef → #f59e0b, diagonale de la boîte englobante Le dégradé suit la boîte, pas le stylo : sur la courbe livrée on ne voit que violet → rose. Pour un dégradé le long du trajet, gradientUnits="userSpaceOnUse" x1="20" x2="240" ; pour une couleur unie, stroke: #d946ef. L'id est global : deux instances aux couleurs différentes exigent deux ids.
Taille et point de fixation (CSS — .pathdraw-sticky { width: 90%; max-width: 360px; top: 10px }, stroke-width: 3) 360 × 277 px au plus, fixé à 10 px du haut Le max-width borne le dessin à la fenêtre de 300 px : sans lui, à 1 280 px de large, le SVG fait 875 px de haut et seul un tiers du tracé est visible. Pour un conteneur plus haut, montez les deux ensemble (hauteur du SVG = largeur × 200 ÷ 260, à garder sous conteneur − 20). Le trait de 3 unités mesure 4,2 px à 360 px.

FAQ

Parce que le tiret est réglé sur 600 unités pour un tracé de 364,42. L'offset descend linéairement de 600 à 0 sur les 1 200 px ; la longueur visible vaut 600 − offset, donc la ligne est finie dès l'offset 235,6 — 60,7 % de la course, 728 px — et plus rien ne change sur elle pendant les 472 px suivants, le temps que les jalons 3 et 4 s'allument. Deux façons de resynchroniser, mesurées : écrire 365 dans stroke-dasharray, stroke-dashoffset et le 0% de drawPath, ou poser pathLength="600" sur le path — l'attribut normalise la longueur du tracé à 600 pour le calcul du tiret (à l'offset 300, le stylo est à mi-chemin dans Chromium, WebKit et Firefox). Le stylo arrive alors au bout à 100 % et passe les jalons 2 et 3 à 38 et 63 %, juste avant leur allumage complet à 40 et 65 %. Les trois scènes de cette page utilisent pathLength.
Le moteur. fx-0781 est en JavaScript : un écouteur scroll sous requestAnimationFrame écrit la progression dans une variable --p, et cinq anneaux calculent leur stroke-dasharray en calc() depuis cette variable, tournent leur teinte en oklch et affichent un pourcentage — tout avance ensemble, proportionnellement, et cela fonctionne dans Firefox puisqu'aucune scroll-timeline n'intervient. fx-0572 n'a pas une ligne de script : la timeline nommée est déclarée sur le conteneur et, surtout, chaque jalon reçoit sa propre plage animation-range — des événements ponctuels (0-15, 25-40, 50-65, 75-90 %) posés sur une ligne continue, ce qu'un seul --p ne donne pas sans une condition par anneau. Le prix : Firefox n'exécute aucune version de la timeline (repli à plat, dessin fini) et la page ne dispose d'aucun nombre à lire — pas de compteur, pas de bouton à débloquer. fx-0781 pour une progression globale et chiffrée qui doit tourner partout ; fx-0572 pour des étapes datées sur un trajet.
Un jalon = un circle de plus avec ses cx/cy (sans r, le CSS le porte) et une règle .pathdraw-dot:nth-of-type(5) { animation-range: … } ; les plages sont libres, il suffit qu'elles se suivent — cinq créneaux de 12 % séparés de 10 % : 0% 12%, 22% 34%, 44% 56%, 66% 78%, 88% 100%. Le compteur nth-of-type ne compte que les circle : le path ne décale rien. Pour les positions, les cx/cy sont des attributs posés à la main — les jalons 2 et 3 livrés flottent 14 et 17 unités au-dessus de la courbe. Dans la console, const p = document.querySelector('.pathdraw-path'); p.getPointAtLength(p.getTotalLength() * 0.38) renvoie le point à 38 % du tracé (celui que le stylo atteint à 38 % avec pathLength="600") : recopiez x et y. Si le nouveau tracé dépasse 600 unités, montez le tiret ou posez pathLength, sinon la ligne s'arrête avant le dernier jalon.