block by pjsier 0cd4e2eb3819e50cffea7b70a8c2cd24

Simple Responsive Gauge

Full Screen

script.js

// Based on reusable chart pattern from https://bost.ocks.org/mike/chart/
function gaugeChart() {
  var margin = {top: 0, right: 65, bottom: 10, left: 65},
      width = 250,
      height = 150,
      arcMin = -Math.PI/2,
      arcMax = Math.PI/2,
      innerRadius = 60,
      outerRadius = 80,
      dataDomain = [0, 50, 100],
      labelPad = 10,
      dataValue = function(d) { return +d; },
      colorScale = d3.scaleLinear(),
      arcScale = d3.scaleLinear(),
      colorOptions = ["#d7191c", "#efef5d", "#1a9641"],
      arc = d3.arc();

  function chart(selection) {
    selection.each(function(data) {
      // Convert data to standard representation greedily;
      // this is needed for nondeterministic accessors.
      data = data.map(function(d, i) { return dataValue(d); });
      arcScale = d3.scaleLinear().domain(dataDomain).range([arcMin, 0, arcMax]);
      colorScale = d3.scaleLinear().domain(dataDomain).range(colorOptions);
      arc = d3.arc().innerRadius(innerRadius)
        .outerRadius(outerRadius)
        .startAngle(arcMin);

      // Select the svg element, if it exists.
      var svg = d3.select(this).selectAll("svg").data([data]);

      // Otherwise, create the skeletal chart.
      var gEnter = svg.enter().append("svg").append("g");
      var arcGEnter = gEnter.append("g").attr("class", "arc");
      arcGEnter.append("path").attr("class", "bg-arc");
      arcGEnter.append("path").attr("class", "data-arc")
        .datum({endAngle: arcMin, startAngle: arcMin, score: dataDomain[0]})
        .attr("d", arc)
        .style("fill", colorScale(dataDomain[0]))
        .each(function(d) { this._current = d; });
      arcGEnter.append("text").attr("class", "arc-label");


      arcGEnter.selectAll(".lines").data(arcScale.ticks(5).map(function(d) {
        return { score: d };
      })).enter()
        .append("path")
          .attr("class", "lines");
      arcGEnter.selectAll(".ticks").data(arcScale.ticks(5))
        .enter().append("text")
          .attr("class", "ticks");

      // Update the outer dimensions.
      var svg = selection.select("svg");
      svg.attr("width", width).attr("height", height);
      // Update the inner dimensions.
      var g = svg.select("g")
          .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

      var arcG = svg.select("g.arc")
        .attr("transform", "translate(" +
          ((width - margin.left - margin.right) / 2) + "," +
          ((height * (2 / 3)) + ")"));

      svg.select("g.arc .bg-arc")
        .datum({endAngle: arcMax})
        .style("fill", "#ddd")
        .attr("d", arc);

      // https://bl.ocks.org/mbostock/1346410
      function arcTween(a) {
        var i = d3.interpolate(this._current, a);
        this._current = i(0);
        return function(t) {
          return arc(i(t));
        };
      }

      var dataArc = svg.select("g.arc .data-arc")
        .datum({score: data[0], startAngle: arcMin, endAngle: arcScale(data[0])})
          .transition()
          .duration(750)
            .style("fill", function(d) { return colorScale(d.score); })
            .style("opacity", function(d) { return d.score < dataDomain[0] ? 0 : 1; })
            .attrTween("d", arcTween);

      var arcBox = svg.select("g.arc .bg-arc").node().getBBox();
      svg.select("text.arc-label")
        .datum({score: data[0]})
        .attr("x", (arcBox.width/2)+arcBox.x)
        .attr("y", -15)
        .style("alignment-baseline", "central")
        .style("text-anchor", "middle")
        .style("font-size", "30px")
        .text(function(d) { return d3.format(".1f")(d.score); });

      var markerLine = d3.radialLine()
        .angle(function(d) { return arcScale(d); })
        .radius(function(d, i) {
          return innerRadius + ((i % 2) * ((outerRadius - innerRadius)));
        });

      arcG.selectAll(".lines")
        .attr("d", function(d) { return markerLine([d.score, d.score]); })
        .style("fill", "none")
        .style("stroke-width", 2.5)
        .style("stroke", "#fff");
      arcG.selectAll(".ticks")
        .style("font-size", "12px")
        .style("text-anchor", "middle")
        .attr("x", function(d) { return Math.cos(arcScale(d) + arcMin) * (outerRadius + labelPad); })
        .attr("y", function(d) {
          var yVal = Math.sin(arcScale(d) + arcMin) * (outerRadius + labelPad);
          return yVal < -1 ? yVal : -7;
        }).text(function(d) { return d; });
    });
  }

  chart.margin = function(_) {
    if (!arguments.length) return margin;
    margin = _;
    return chart;
  };

  chart.width = function(_) {
    if (!arguments.length) return width;
    width = _;
    return chart;
  };

  chart.height = function(_) {
    if (!arguments.length) return height;
    height = _;
    return chart;
  };

  chart.innerRadius = function(_) {
    if (!arguments.length) return innerRadius;
    innerRadius = _;
    return chart;
  };

  chart.outerRadius = function(_) {
    if (!arguments.length) return outerRadius;
    outerRadius = _;
    return chart;
  };

  chart.dataDomain = function(_) {
    if (!arguments.length) return dataDomain;
    dataDomain = _;
    return chart;
  };

  chart.colorOptions = function(_) {
    if (!arguments.length) return colorOptions;
    colorOptions = _;
    return chart;
  };

  chart.labelPad = function(_) {
    if (!arguments.length) return labelPad;
    labelPad = _;
    return chart;
  };

  return chart;
}

index.html

<!DOCTYPE html>
<html>
  <head>
    <title>Simple Gauge Visualization</title>
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
    <meta charset='utf-8' />
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <style>
    text {
      font-family: "Verdana", sans-serif;
    }
    #chart {
      max-width: 500px;
    }
    </style>
  </head>
  <body>
    <button id="random">Random Number</button>
    <div id="chart"></div>
    <script src="script.js"></script>
    <script>
    function randNumberBounds(min, max) {
      min = Math.ceil(min);
      max = Math.floor(max);
      return Math.floor(Math.random() * (max - min)) + min;
    }

    (function() {
      var gauge = gaugeChart()
        .width(260)
        .height(200)
        .innerRadius(50)
        .outerRadius(80);

      d3.select("#chart").datum([randNumberBounds(0, 100)]).call(gauge);

      function resize() {
        var gWidth = Math.min(d3.select("#chart").node().offsetWidth, 260);
        gauge.width(gWidth).innerRadius(gWidth / 4).outerRadius((gWidth / 4) + 40);
        d3.select("#chart").call(gauge);
      }

      resize();
      window.addEventListener("resize", resize);

      var button = document.getElementById("random");
      button.addEventListener("click", function() {
        d3.select("#chart").datum([randNumberBounds(0, 100)]).call(gauge);
      });
    })()
    </script>
</body>
</html>