Published
Edited
Oct 20, 2021
3 stars
Insert cell
Insert cell
Insert cell
Insert cell
chart = {
const svg = d3.create("svg")
.attr("viewBox", [-width/2, -height/2, width, height]);
let backgroundBar = svg.append("g").append("path")
.datum({endAngle: r.range()[1]})
.attr("d", drawBar)
.classed("bar-background", true);
let ticksData = c.domain() // e.g., [70, 130]
.map((e, i) => {
return {value: e, index: i, startAngle: r(e), endAngle: r(e)};
});
let ticks = svg.append("g")
.selectAll("path")
.data(ticksData)
.join("path")
.attr("d", drawTick)
.classed("tick", true)
.attr("stroke-width", tickStrokeWidth);
let foregroundBar = svg.append("g").append("path")
.datum({
value: r.domain()[0],
endAngle: r(r.domain()[0])
})
.attr("d", drawBar)
.classed("bar-foreground", true);
let text = svg.append("g").append("text")
.attr("text-anchor", "middle")
.attr("alignment-baseline", "middle")
.classed("text-value", true)
.text("-");

// update value
function update(value) {
text
.call(toggleClassesInSelection, c(value), c.range())
.text(value);
const t = svg.transition()
.duration(750)
.ease(d3.easeBackOut);
foregroundBar
.datum({value: value})
.call(toggleClassesInSelection, c(value), c.range())
.transition(t)
.attrTween("d", arcTween(r(value)));

}
function arcTween(newAngle) {
return function(d) {
let interpolate = d3.interpolate(r.range()[0], newAngle);
return function(t) {
d.endAngle = interpolate(t);
return drawBar(d);
};
};
};

return Object.assign(svg.node(), { update });
}
Insert cell
chart.update(inputValue);
Insert cell
Insert cell
drawBar = d3.arc()
.innerRadius(radiusToCenter + barWidth/2)
.outerRadius(radiusToCenter - barWidth/2)
.startAngle(r.range()[0])
.cornerRadius(barWidth / 2);
Insert cell
drawTick = d3.arc()
.innerRadius(radiusToCenter + tickWidth/2)
.outerRadius(radiusToCenter - tickWidth/2);
Insert cell
Insert cell
// maps domain value to angle size in radians.. direct input to draw functions
r = {
const span = (spanInDegrees / 360) * tau;
return d3.scaleLinear()
.domain([0, 200])
.range([span * -0.5, span*0.5]);
}
Insert cell
// maps domain value to color classname, based on 'range' variable
c = {
return d3.scaleThreshold()
.domain(inputRange)
.range(["eval-lower", "eval-neutral", "eval-higher"]);
};
Insert cell
Insert cell
ticksData = c.domain().map((e, i) => {
return {value: e, index: i, startAngle: r(e), endAngle: r(e)};
});
Insert cell
Insert cell
toggleClassesInSelection = (sel, newClass, classList) => {
sel.classed(classList.filter(e => e != newClass).join(" "), false);
sel.classed(newClass, true);
};
Insert cell
<style type="text/css">
.tick {
stroke: rgb(192, 192, 192);
stroke-linecap: round;
stroke-linejoin: round;
}
.bar-background {
fill: rgb(192, 192, 192);
}
.bar-foreground {
;
}
.text-value {
font-family: "Arial Narrow", sans-serif;
font-size: 52pt;
font-weight: light;
}
.eval-higher {
fill: rgb(0, 157, 156);
}
.eval-neutral {
fill: rgb(77, 77, 77);
}
.eval-lower {
fill: rgb(250, 114, 104);
}
</style>
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more