SPB Git

spb/svgarden Public

SVGarden — searchable bank of 74 self-contained SVG+CSS animation snippets (svgarden.dev)

HTML 79.2% Astro 10.3% JavaScript 6% CSS 3.7% Shell 0.8%
3.7 KB · 102 lines html
Raw Blame History
1<!--2  ============================================================3  SVGarden — https://www.svgarden.dev4  Author : Simon-Pierre Boucher5  Contact: contact@spboucher.ai6  File   : snippets/scroll/scroll-progress-path.html7  Desc   : A path draws itself as its container scrolls (scroll-driven animation)8  ============================================================9-->10<!--svgarden11title: Scroll-drawn path12slug: scroll-progress-path13category: scroll14tags: [scroll, draw, progress, scroll-timeline]15difficulty: advanced16techniques: ["animation-timeline: scroll()", "@supports", pathLength, stroke-dashoffset]17how_it_works: >18  animation-timeline: scroll() detaches the keyframes from the clock and binds19  them to scroll progress — scroll(nearest) means "the nearest scrolling20  ancestor", which here is the snippet's own overflow-y box, so the demo is21  fully self-contained. The dashoffset keyframes then map 0%→100% of the22  scroll range onto the classic line-draw trick, and animation-duration: auto23  hands timing control entirely to the timeline. Everything sits inside an24  @supports gate: browsers without scroll-driven animations simply show the25  path fully drawn instead of broken.26support: "Scroll-driven animations need Chrome/Edge 115+ (Firefox behind a flag); unsupported browsers show the path fully drawn, static."27customizable:28  - { var: "--sg-color",  label: "Ink",       type: color, default: "#7F77DD" }29  - { var: "--sg-size",   label: "Box height", type: range, min: 180, max: 400, default: 260, unit: px }30  - { var: "--sg-stroke", label: "Stroke",     type: range, min: 1, max: 8, step: 0.5, default: 3, unit: px }31created: 2026-08-1032-->33<div class="sg-scroll-progress-path" style="--sg-color:#7F77DD; --sg-size:260px; --sg-stroke:3px;">34  <div class="sg-scroll-progress-path-track">35    <div class="sg-scroll-progress-path-sticky">36      <svg viewBox="0 0 200 120" aria-hidden="true" focusable="false">37        <path class="sg-scroll-progress-path-ghost" pathLength="100"38          d="M12 90 C 40 20, 60 20, 80 60 C 100 100, 120 100, 140 40 C 150 15, 170 25, 188 45" />39        <path class="sg-scroll-progress-path-line" pathLength="100"40          d="M12 90 C 40 20, 60 20, 80 60 C 100 100, 120 100, 140 40 C 150 15, 170 25, 188 45" />41      </svg>42      <span class="sg-scroll-progress-path-hint">scroll ↓</span>43    </div>44  </div>45</div>46<style>47  .sg-scroll-progress-path {48    width: 100%;49    min-width: 220px;50    height: var(--sg-size);51    overflow-y: scroll;52    border: 1px solid rgba(128, 128, 128, 0.3);53    border-radius: 10px;54  }55  .sg-scroll-progress-path-track {56    height: calc(var(--sg-size) * 3);57  }58  .sg-scroll-progress-path-sticky {59    position: sticky;60    top: 0;61    height: var(--sg-size);62    display: grid;63    place-items: center;64  }65  .sg-scroll-progress-path-sticky svg {66    width: 78%;67    display: block;68  }69  .sg-scroll-progress-path-hint {70    position: absolute;71    right: 12px;72    bottom: 8px;73    font-size: 12px;74    opacity: 0.6;75  }76  .sg-scroll-progress-path-ghost {77    fill: none;78    stroke: rgba(128, 128, 128, 0.25);79    stroke-width: var(--sg-stroke);80    stroke-linecap: round;81  }82  .sg-scroll-progress-path-line {83    fill: none;84    stroke: var(--sg-color);85    stroke-width: var(--sg-stroke);86    stroke-linecap: round;87    stroke-dasharray: 100;88    stroke-dashoffset: 0; /* fallback: fully drawn */89  }90  @supports (animation-timeline: scroll()) {91    .sg-scroll-progress-path-line {92      animation: sg-scroll-progress-path-draw linear both;93      animation-duration: auto;94      animation-timeline: scroll(nearest);95    }96  }97  @keyframes sg-scroll-progress-path-draw {98    from { stroke-dashoffset: 100; }99    to   { stroke-dashoffset: 0; }100  }101</style>102