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.1 KB · 94 lines html
Raw Blame History
1<!--2  ============================================================3  SVGarden — https://www.svgarden.dev4  Author : Simon-Pierre Boucher5  Contact: contact@spboucher.ai6  File   : snippets/gauges/gauge-circle.html7  Desc   : Circular percentage gauge driven by a range slider (JS)8  ============================================================9-->10<!--svgarden11title: Circle gauge12slug: gauge-circle13category: gauges14tags: [gauge, percentage, slider, js]15difficulty: intermediate16techniques: [pathLength, stroke-dashoffset, css-transition, minimal-js]17how_it_works: >18  With pathLength="100" the circle's circumference *is* 100, so showing v percent19  is just stroke-dashoffset = 100 − v — no trigonometry, no getTotalLength().20  The tiny script only copies the slider value onto that one CSS property and21  the text label; the smooth sweep between values comes for free from a CSS22  transition on stroke-dashoffset. The arc starts at 12 o'clock because the23  whole circle is rotated −90°.24customizable:25  - { var: "--sg-color", label: "Color", type: color, default: "#7F77DD" }26  - { var: "--sg-size",  label: "Size",  type: range, min: 80, max: 220, default: 130, unit: px }27created: 2026-08-1028-->29<div class="sg-gauge-circle" style="--sg-color:#7F77DD; --sg-size:130px;">30  <svg viewBox="0 0 100 100" role="img" aria-label="Percentage gauge">31    <title>Percentage gauge</title>32    <circle class="sg-gauge-circle-track" cx="50" cy="50" r="44" pathLength="100" />33    <circle class="sg-gauge-circle-arc" cx="50" cy="50" r="44" pathLength="100" />34    <text class="sg-gauge-circle-value" x="50" y="57">67%</text>35  </svg>36  <input class="sg-gauge-circle-slider" type="range" min="0" max="100" value="67" aria-label="Gauge value" />37</div>38<script>39  document.querySelectorAll('.sg-gauge-circle:not([data-sg-init])').forEach(function (root) {40    root.setAttribute('data-sg-init', '');41    var arc = root.querySelector('.sg-gauge-circle-arc');42    var label = root.querySelector('.sg-gauge-circle-value');43    var slider = root.querySelector('.sg-gauge-circle-slider');44    function render() {45      arc.style.strokeDashoffset = 100 - slider.value;46      label.textContent = slider.value + '%';47    }48    slider.addEventListener('input', render);49    render();50  });51</script>52<style>53  .sg-gauge-circle {54    width: var(--sg-size);55    display: flex;56    flex-direction: column;57    gap: 0.6rem;58    align-items: center;59  }60  .sg-gauge-circle svg {61    width: 100%;62    display: block;63  }64  .sg-gauge-circle-track,65  .sg-gauge-circle-arc {66    fill: none;67    stroke-width: 8;68    stroke-linecap: round;69    transform: rotate(-90deg);70    transform-origin: 50% 50%;71  }72  .sg-gauge-circle-track {73    stroke: var(--sg-color);74    opacity: 0.15;75  }76  .sg-gauge-circle-arc {77    stroke: var(--sg-color);78    stroke-dasharray: 100;79    stroke-dashoffset: 33;80    transition: stroke-dashoffset 0.45s ease;81  }82  .sg-gauge-circle-value {83    font-family: inherit;84    font-size: 20px;85    font-weight: 600;86    fill: currentColor;87    text-anchor: middle;88  }89  .sg-gauge-circle-slider {90    width: 90%;91    accent-color: var(--sg-color);92  }93</style>94