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

D3 Gauge Chart 如何添加四区段图例、用旋钮代替指针并显示居中数值

D3.js仪表盘自定义修改方案

修改对应需求

  • 用旋钮替换原有指针:删除原三角指针逻辑,改为圆形旋钮沿刻度环随数值移动
  • 新增颜色区段图例:在仪表盘底部添加4组图例,每个颜色块对应区段说明
  • 中心显示分数与等级:仪表盘中心新增两行文本,分别展示当前分数、对应区段等级名称

完整实现代码

HTML

<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<div class="chart-gauge"></div>

CSS

.chart-gauge {
  width: 400px;
  margin: 50px auto;
}
.chart-first {
  fill: #EA4335;
}
.chart-second {
  fill: #FBBC05;
}
.chart-third {
  fill: #4285F4;
}
.chart-fourth {
  fill: #34A853;
}
.knob {
  fill: #222;
  stroke: #fff;
  stroke-width: 2;
}
.center-score {
  font-size: 32px;
  font-weight: bold;
  text-anchor: middle;
}
.center-level {
  font-size: 16px;
  text-anchor: middle;
}
.legend-item rect {
  width: 15px;
  height: 15px;
}
.legend-item text {
  font-size: 12px;
  fill: #333;
}
svg {
  font: 10px sans-serif;
}

JavaScript

// 配置项
var gaugeConfig = {
  value: 17,
  maxValue: 100,
  levelList: [
    { name: "较差", colorClass: "chart-first", min: 0, max: 25 },
    { name: "中等", colorClass: "chart-second", min: 25, max: 50 },
    { name: "良好", colorClass: "chart-third", min: 50, max: 75 },
    { name: "优秀", colorClass: "chart-fourth", min: 75, max: 100 }
  ]
}
var percentValue = gaugeConfig.value / gaugeConfig.maxValue;

(function () {
  var barWidth, chart, chartInset, degToRad, repaintGauge,
  height, margin, numSections, padRad, percToDeg, percToRad,
  percent, radius, svg, totalPercent, width, knob;

  percent = percentValue;
  numSections = 4;
  padRad = 0.025;
  chartInset = 10;
  totalPercent = .75;
  el = d3.select(".chart-gauge");
  margin = {
    top: 20,
    right: 20,
    bottom: 50,
    left: 20 };
  width = el[0][0].offsetWidth - margin.left - margin.right;
  height = width + 30;
  radius = Math.min(width, width) / 2;
  barWidth = 40 * width / 300;

  // 工具方法
  percToDeg = function (perc) {
    return perc * 360;
  };
  percToRad = function (perc) {
    return degToRad(percToDeg(perc));
  };
  degToRad = function (deg) {
    return deg * Math.PI / 180;
  };
  // 获取当前数值对应的等级
  function getCurrentLevel(val) {
    for(var i=0;i<gaugeConfig.levelList.length;i++) {
      if(val >= gaugeConfig.levelList[i].min && val <= gaugeConfig.levelList[i].max) {
        return gaugeConfig.levelList[i];
      }
    }
    return gaugeConfig.levelList[0];
  }

  // 创建SVG
  svg = el.append("svg").attr("width", width + margin.left + margin.right).attr("height", height + margin.top + margin.bottom);
  chart = svg.append("g").attr("transform", "translate(" + (width + margin.left) / 2 + ", " + (width + margin.top) / 2 + ")");

  // 绘制刻度弧
  chart.append("path").attr("class", "arc chart-first");
  chart.append("path").attr("class", "arc chart-second");
  chart.append("path").attr("class", "arc chart-third");
  chart.append("path").attr("class", "arc chart-fourth");
  var arc = d3.svg.arc().outerRadius(radius - chartInset).innerRadius(radius - chartInset - barWidth);

  repaintGauge = function () {
    var perc = 0.5;
    var next_start = totalPercent;
    for(var i=1;i<=4;i++) {
      var arcStartRad = percToRad(next_start);
      var arcEndRad = arcStartRad + percToRad(perc / 4);
      next_start += perc / 4;
      if(i>1) arcStartRad += padRad;
      arc.startAngle(arcStartRad).endAngle(arcEndRad);
      chart.select(".chart-"+["first","second","third","fourth"][i-1]).attr("d", arc);
    }
  }
  repaintGauge();

  // 绘制刻度值
  svg.append("text").text(0).attr("x", 10).attr("y", (width + margin.top)/2 + 5).attr("font-size", 15).style("fill", "#000");
  svg.append("text").text(gaugeConfig.maxValue/2).attr("x", (width + margin.left)/2 - 10).attr("y", 20).attr("font-size", 15).style("fill", "#000");
  svg.append("text").text(gaugeConfig.maxValue).attr("x", width + margin.left - 20).attr("y", (width + margin.top)/2 + 5).attr("font-size", 15).style("fill", "#000");

  // 绘制旋钮
  knob = chart.append("circle")
    .attr("class", "knob")
    .attr("r", 8)
    .attr("cx", 0)
    .attr("cy", 0);
  // 旋钮移动逻辑
  function moveKnob(perc) {
    var thetaRad = percToRad(perc / 2);
    var knobRadius = radius - chartInset - barWidth/2;
    var x = -knobRadius * Math.cos(thetaRad);
    var y = -knobRadius * Math.sin(thetaRad);
    knob.transition().delay(300).ease("bounce").duration(1500)
      .attr("cx", x)
      .attr("cy", y);
  }
  moveKnob(percent);

  // 绘制中心文本
  var currentLevel = getCurrentLevel(gaugeConfig.value);
  chart.append("text")
    .attr("class", "center-score")
    .attr("x", 0)
    .attr("y", 0)
    .text(gaugeConfig.value);
  chart.append("text")
    .attr("class", "center-level")
    .attr("x", 0)
    .attr("y", 20)
    .text(currentLevel.name);

  // 绘制图例
  var legend = svg.append("g").attr("transform", "translate("+(margin.left)+"," + (width + margin.top + 10) + ")");
  var legendItems = legend.selectAll(".legend-item")
    .data(gaugeConfig.levelList)
    .enter().append("g")
    .attr("class", "legend-item")
    .attr("transform", function(d,i) { return "translate(" + (i * 80) + ", 0)"; });
  legendItems.append("rect")
    .attr("x", 0)
    .attr("y", 0)
    .attr("class", function(d) { return d.colorClass; });
  legendItems.append("text")
    .attr("x", 20)
    .attr("y", 12)
    .text(function(d) { return d.min + "-" + d.max + " " + d.name; });
})();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 13:54:03