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%
4.6 KB · 113 lines html
Raw Blame History
1<!--2  ============================================================3  SVGarden — https://www.svgarden.dev4  Author : Simon-Pierre Boucher5  Contact: contact@spboucher.ai6  File   : snippets/charts/chart-sparkline-live.html7  Desc   : Live sparkline — new points stream in, old ones slide out8  ============================================================9-->10<!--svgarden11title: Live sparkline12slug: chart-sparkline-live13category: charts14tags: [chart, sparkline, live, streaming, js]15difficulty: advanced16techniques: ["fixed-window data buffer", "points attribute rewrite", "transition retrigger", prefers-reduced-motion]17how_it_works: >18  The script keeps a fixed-length array as a sliding window: every tick pushes a19  new random-walk value, shifts the oldest out, and rewrites the polyline's20  points attribute from scratch — with 22 points that is far cheaper than any21  clever incremental DOM surgery. The slide illusion is a transition retrigger22  trick: after each rewrite the group is jumped 10 units right with transitions23  disabled (the new geometry then overlaps the old frame exactly), and one24  requestAnimationFrame later the transition is re-enabled and the group eases25  back to zero. The tick delay is re-read from --sg-duration on every cycle via26  a setTimeout chain, so the speed slider takes effect on the very next point,27  and prefers-reduced-motion stops the stream after the first paint.28customizable:29  - { var: "--sg-color",    label: "Line color", type: color, default: "#7F77DD" }30  - { var: "--sg-size",     label: "Width",      type: range, min: 140, max: 380, default: 220, unit: px }31  - { var: "--sg-duration", label: "Tick every", type: range, min: 0.3, max: 2, step: 0.1, default: 0.7, unit: s }32created: 2026-08-1033-->34<div class="sg-chart-sparkline-live" style="--sg-color:#7F77DD; --sg-size:220px; --sg-duration:0.7s;">35  <svg viewBox="0 0 220 60" role="img" aria-label="Live-updating sparkline of streaming values">36    <title>Live sparkline</title>37    <line class="sg-chart-sparkline-live-base" x1="5" y1="58" x2="215" y2="58" />38    <g class="sg-chart-sparkline-live-win">39      <polygon class="sg-chart-sparkline-live-area" points="" />40      <polyline class="sg-chart-sparkline-live-line" points="" />41    </g>42    <circle class="sg-chart-sparkline-live-dot" cx="215" cy="30" r="3" />43  </svg>44</div>45<script>46  document.querySelectorAll('.sg-chart-sparkline-live:not([data-sg-init])').forEach(function (root) {47    root.setAttribute('data-sg-init', '');48    var line = root.querySelector('.sg-chart-sparkline-live-line');49    var area = root.querySelector('.sg-chart-sparkline-live-area');50    var win = root.querySelector('.sg-chart-sparkline-live-win');51    var dot = root.querySelector('.sg-chart-sparkline-live-dot');52    var N = 22, data = [30];53    for (var i = 1; i < N; i++) data.push(Math.max(8, Math.min(52, data[i - 1] + Math.random() * 14 - 7)));54    function render() {55      var pts = data.map(function (v, i) { return (5 + i * 10) + ',' + (58 - v).toFixed(1); }).join(' ');56      line.setAttribute('points', pts);57      area.setAttribute('points', pts + ' 215,58 5,58');58      dot.setAttribute('cy', (58 - data[N - 1]).toFixed(1));59    }60    render();61    if (matchMedia('(prefers-reduced-motion: reduce)').matches) return;62    function tick() {63      data.push(Math.max(8, Math.min(52, data[N - 1] + Math.random() * 14 - 7)));64      data.shift();65      win.style.transition = 'none';66      render();67      win.style.transform = 'translateX(10px)';68      requestAnimationFrame(function () {69        var s = parseFloat(getComputedStyle(root).getPropertyValue('--sg-duration')) || 0.7;70        win.style.transition = 'transform ' + s + 's linear';71        win.style.transform = 'translateX(0)';72        setTimeout(tick, s * 1000);73      });74    }75    tick();76  });77</script>78<style>79  .sg-chart-sparkline-live {80    width: var(--sg-size);81  }82  .sg-chart-sparkline-live svg {83    display: block;84    width: 100%;85    overflow: hidden;86  }87  .sg-chart-sparkline-live-base {88    stroke: rgba(128, 128, 128, 0.3);89    stroke-width: 1;90  }91  .sg-chart-sparkline-live-line {92    fill: none;93    stroke: var(--sg-color);94    stroke-width: 2;95    stroke-linecap: round;96    stroke-linejoin: round;97  }98  .sg-chart-sparkline-live-area {99    fill: var(--sg-color);100    opacity: 0.12;101  }102  .sg-chart-sparkline-live-dot {103    fill: var(--sg-color);104    transform-box: fill-box;105    transform-origin: center;106    animation: sg-chart-sparkline-live-pulse 1.4s ease-in-out infinite;107  }108  @keyframes sg-chart-sparkline-live-pulse {109    0%, 100% { transform: scale(1); opacity: 1; }110    50%      { transform: scale(1.7); opacity: 0.55; }111  }112</style>113