You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现支持API返回动态不等距刻度的SVG半圆仪表盘

非等距动态刻度SVG仪表盘实现方案

需求说明

我正在尝试制作如下效果的SVG仪表盘:
参考效果图

  • 刻度完全动态,由API返回
  • 刻度放置在接口返回的百分比数值数组对应的位置,无需有序
  • 两个刻度之间的间距无需相等

初始实现尝试

初始版本使用半圆加stroke-dasharray实现,但原逻辑只支持等距刻度,不符合非等距需求:

初始JS代码

let divisionsElement = document.getElementById('divisions');
let length = parseInt(divisionsElement.getAttribute("r")) * Math.PI;
let divisionsArray = [20,30,80,90]; //stroke at 20%, 30%,80%,90% position
let METER_DIVISIONS_GAP = 0.2;

divisionsArray.map(ele => {
    setupPath(ele);
})


function setupPath(val) {
    divisionsElement.setAttribute("stroke-dasharray", val + ' ' +METER_DIVISIONS_GAP);
}

初始CSS代码

circle {
  fill: none;
}
.circle-back {
  stroke: #F8F6F0;
  stroke-width:9px;
}

.circle-fill {
  stroke: grey;
  stroke-width: 10px;
}

.circle-progress {
  stroke: blue;
  stroke-width:9px;
}

初始HTML代码

<svg viewBox="0 0 126 126" preserveAspectRatio="xMinYMin meet">

  <clipPath id="cut-off">
    <rect x="0" y="0" width="100%" height="50" />
  </clipPath>

  <clipPath id="progress-percent">
    <path x="0" y="0" width="12%" height="50" />
  </clipPath>
  
  <g>
    <circle r="35%" cx="40%" cy="40%" class="circle-fill" clip-path="url(#cut-off)" />

    <circle id="divisions" r="35%" cx="40%" cy="40%" class="circle-back" clip-path="url(#cut-off)" 
    stroke-dasharray="20 0.5" stroke-dashoffset="25" />

    <circle r="35%" cx="40%" cy="40%" class="circle-progress" clip-path="url(#progress-percent)" />
  </g>
  
</svg>

IE兼容优化方案

如果需要兼容IE,可以使用以下基于path路径的实现方案:

兼容版JS代码

var data = [10,20,10,10,25,25];
var dataSum = 0;
for (let i = 0; i < data.length - 1; i++) { dataSum += data[i]; }



var drawArray = "";
var indexOfFill = 2;
var arcPath = document.getElementById("meter-back");
var filledPath = document.getElementById("meter-fill");

var totalLength = arcPath.getTotalLength();
    function drawPath() {

  // Draw the background strokes
  let test='';
   for (i = 0; i < data.length; i++) {
     let barLen = ((data[i]) * totalLength) / 100;
     test = test + ((barLen) - (0.2 * (dataSum / 100)) + " " + 0.2 * (dataSum / 100) + " ")
  }
  arcPath.setAttribute("stroke-dasharray", test);
  animate(filledPath, totalLength, 0,50);
  }drawPath();
  
  
function animate(el,length,end,percentage) {
    el.style.visibility = 'visible';
    if (end <= percentage) {
          el.style.strokeOpacity = 5;
          let gap = 100 - end;
          let offset = 100;
          el.style.strokeDasharray = ((end / 100) * length) + ' ' + ((gap / 100) * length);
          el.style.strokeDashoffset = (offset / 100) * length;

          animate(el, length, ++end, percentage);
        }

}  

兼容版HTML代码

<svg width="100%" height="600px" viewBox="0 0 200 200" preserveAspectRatio="xMinYMin meet">
  <defs>
    <filter id="outline" y="-50%" height="200%">
      <feMorphology operator="erode" radius="0.2" />
      <feComposite operator="out" in="SourceGraphic" result="outline" />
      <feFlood flood-color="grey" />
      <feComposite operator="in" in2="outline" />
      <feComposite operator="atop" in2="SourceGraphic" />
    </filter>
  </defs>

  <g filter="url(#outline)">
    <path fill="none" stroke-dasharray="" stroke-width="20" stroke="grey" d="M30 100 A 40 40 0 0 1 170 100" />
    <path id="meter-back" fill="none" stroke-dasharray="" stroke-width="20" stroke="white" d="M30 100 A 40 40 0 0 1 170 100" />
    <path id="meter-fill" fill="none" stroke-dasharray="" stroke="rgba(128,128,128,0.5)" stroke-width="20" d="M30 100 A 40 40 0 0 1 170 100" style="visibility: hidden;" ></path>
  </g>
  

</svg>

内容的提问来源于stack exchange,提问作者Kshri

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 23:36:08