Last active
April 13, 2021 02:15
-
-
Save luciyer/44f77cc54f3a48be83581b637e36ceb5 to your computer and use it in GitHub Desktop.
Configurable Gauge Chart using D3.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* DEMO: https://observablehq.com/@luciyer/exportable-gauge */ | |
/* USAGE */ | |
let chart = GaugeChart(); | |
// Optionally - set properties - these are defaults: | |
/* | |
chart.setProperties({ | |
rotation: 0, | |
thickness: 0.15, | |
arc: 1.15, | |
ticks: 11, | |
color_scheme: "interpolateRdYlBu", | |
color_step: 150, | |
tick_color: "#FFF", | |
needle_color: "#000" | |
}); | |
*/ | |
chart.setPercentage(0.68); | |
var svg = chart.draw(); | |
yield svg.node(); | |
/* FUNCTION */ | |
function GaugeChart() { | |
var pi = Math.PI, | |
rad = pi/180, | |
deg = 180/pi; | |
var properties = { | |
width: 800, | |
height: 300, | |
margin: 50, | |
rotation: 0, | |
thickness: 0.15, | |
arc: 1, | |
ticks: 5, | |
color_scheme: "interpolateRdYlBu", | |
color_step: 150, | |
tick_color: "#FFFFFF", | |
needle_color: "#000000" | |
}; | |
var needlePercent = 0, | |
center = {}, | |
radii = {}, | |
angles = {}, | |
ticks = {}, | |
gradient = [], | |
scales = {}; | |
var setCenter = (function initCenter () { | |
center.x = properties.width / 2, | |
center.y = properties.height - properties.margin; | |
return initCenter; | |
})(); | |
var setRadii = (function initRadii () { | |
var base = properties.height - (2 * properties.margin); | |
radii.base = base, | |
radii.cap = base / 15, | |
radii.inner = base * (1 - properties.thickness), | |
radii.outer_tick = base + 5, | |
radii.tick_label = base + 15; | |
return initRadii; | |
})(); | |
var setAngles = (function initAngles () { | |
var arc_complement = 1 - properties.arc; | |
angles.arc_complement = arc_complement, | |
angles.start_angle = (-pi/2) + (pi * arc_complement / 2) + (properties.rotation * rad), | |
angles.end_angle = (pi/2) - (pi * arc_complement / 2) + (properties.rotation * rad); | |
return initAngles; | |
})(); | |
var setTicks = (function initTicks () { | |
var sub_arc = (angles.end_angle - angles.start_angle) / (properties.ticks - 1), | |
tick_pct = 100 / (properties.ticks - 1); | |
ticks = d3.range(properties.ticks).map(function(d) { | |
var sub_angle = angles.start_angle + (sub_arc * d); | |
return { | |
label: (tick_pct * d).toFixed(0) + '%', | |
angle: sub_angle, | |
coordinates: [[sub_angle, radii.inner], | |
[sub_angle, radii.outer_tick]] | |
} | |
}); | |
return initTicks; | |
})(); | |
var setGradient = (function initGradient () { | |
var c = d3[properties.color_scheme], | |
samples = properties.color_step, | |
total_arc = angles.end_angle - angles.start_angle, | |
sub_arc = total_arc / (samples); | |
gradient = d3.range(samples).map(function(d) { | |
var sub_color = d / (samples - 1), | |
sub_start_angle = angles.start_angle + (sub_arc * d), | |
sub_end_angle = sub_start_angle + sub_arc; | |
return { | |
fill: c(sub_color), | |
start: sub_start_angle, | |
end: sub_end_angle | |
} | |
}); | |
return initGradient; | |
})(); | |
var setScales = (function initScales () { | |
scales.lineRadial = d3.lineRadial(); | |
scales.subArcScale = d3.arc() | |
.innerRadius(radii.inner + 1) | |
.outerRadius(radii.base) | |
.startAngle(d => d.start) | |
.endAngle(d => d.end); | |
scales.needleScale = d3.scaleLinear() | |
.domain([0, 1]) | |
.range([angles.start_angle, angles.end_angle]); | |
return initScales; | |
})(); | |
function updateValues () { | |
setCenter(); | |
setRadii(); | |
setAngles(); | |
setTicks(); | |
setGradient(); | |
setScales(); | |
} | |
var GaugeChart = {}; | |
GaugeChart.setProperties = function(params) { | |
Object.keys(params).map(function(d) { | |
if (d in properties) | |
properties[d] = params[d]; | |
else | |
throw new Error('One or more parameters not accepted.'); | |
}); updateValues(); | |
} | |
GaugeChart.getProperties = function () { | |
return properties; | |
} | |
GaugeChart.debug = function () { | |
return { needlePercent, properties, center, radii, angles, ticks, gradient, svg }; | |
} | |
GaugeChart.setPercentage = function (pct) { | |
needlePercent = pct; | |
} | |
GaugeChart.draw = function () { | |
var svg = d3.create("svg") | |
.attr("viewBox", [0, 0, properties.width, properties.height]) | |
var gauge = svg.append("g") | |
.attr("transform", `translate(${center.x}, ${center.y})`) | |
.attr("class", "gauge-container"); | |
gauge.append("g") | |
.attr("class", "gauge-arc") | |
.selectAll("path") | |
.data(gradient) | |
.enter() | |
.append("path") | |
.attr("d", scales.subArcScale) | |
.attr("fill", d => d.fill) | |
.attr("stroke-width", 0.5) | |
.attr("stroke", d => d.fill); | |
gauge.append("g") | |
.attr("class", "gauge-ticks") | |
.selectAll("path") | |
.data(ticks) | |
.enter() | |
.append("g") | |
.attr("class", "tick") | |
.append("path") | |
.attr("d", d => scales.lineRadial(d.coordinates)) | |
.attr("stroke", properties.tick_color) | |
.attr("stroke-width", 2) | |
.attr("stroke-linecap", "round") | |
.attr("fill", "none"); | |
gauge.select("g.gauge-ticks") | |
.selectAll("text") | |
.data(ticks) | |
.enter() | |
.append("g") | |
.attr("class", "tick-label") | |
.append("text") | |
.attr("transform", d => | |
`translate(${radii.tick_label * Math.sin(d.angle)}, | |
${-radii.tick_label * Math.cos(d.angle)}) | |
rotate(${d.angle * deg - pi})`) | |
.attr("dy", "0.35em") | |
.attr("text-anchor", "middle") | |
.attr("font-size", "0.67em") | |
.text(d => d.label); | |
gauge.append("g") | |
.attr("class", "needle") | |
.selectAll("path") | |
.data([needlePercent]) | |
.enter() | |
.append("path") | |
.attr("d", d => scales.lineRadial([[0,0], [scales.needleScale(d), radii.outer_tick]])) | |
.attr("stroke", properties.needle_color) | |
.attr("stroke-width", 6) | |
.attr("stroke-linecap", "round"); | |
gauge.select("g.needle") | |
.append("circle") | |
.attr("cx", 0) | |
.attr("cy", 0) | |
.attr("r", radii.cap) | |
.attr("stroke", properties.needle_color) | |
.attr("stroke-width", 6) | |
.style("fill", "white"); | |
return svg; | |
} | |
return GaugeChart; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment