Skip to content

Instantly share code, notes, and snippets.

@pjsier pjsier/.block

Last active May 23, 2020
Embed
What would you like to do?
Simple Responsive Gauge
license: mit
<!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>
// 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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.