Skip to content

Instantly share code, notes, and snippets.

@AntonOrlov
Last active February 28, 2024 14:57
Show Gist options
  • Save AntonOrlov/6b42d8676943cc933f48a43a7c7e5b6c to your computer and use it in GitHub Desktop.
Save AntonOrlov/6b42d8676943cc933f48a43a7c7e5b6c to your computer and use it in GitHub Desktop.
Radial Bar Chart built with D3
license: gpl-3.0
name value
Jan 432
Feb 340
Mar 382
Apr 398
May 410
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Household monthly electricity consumption</title>
<style>
body {
font: 12px sans-serif;
}
svg {
margin: 0px auto;
display: block;
}
path.arc {
opacity: 0.9;
transition: opacity 0.5s;
}
path.arc:hover {
opacity: 0.7;
}
.axis line, .axis circle {
stroke: #cccccc;
stroke-width: 1px
}
.axis circle {
fill: none;
}
.r.axis text {
text-anchor: end
}
.tooltip {
position: absolute;
display: none;
background: rgba(0, 0, 0, 0.6);
border-radius: 3px;
box-shadow: -3px 3px 15px #888;
color: white;
padding: 6px;
}
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<script>
const width = 960,
height = 500,
chartRadius = height / 2 - 40;
const color = d3.scaleOrdinal(d3.schemeCategory10);
let svg = d3.select('body').append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
let tooltip = d3.select('body').append('div')
.attr('class', 'tooltip');
const PI = Math.PI,
arcMinRadius = 10,
arcPadding = 10,
labelPadding = -5,
numTicks = 10;
d3.csv('energy.csv', (error, data) => {
let scale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value) * 1.1])
.range([0, 2 * PI]);
let ticks = scale.ticks(numTicks).slice(0, -1);
let keys = data.map((d, i) => d.name);
//number of arcs
const numArcs = keys.length;
const arcWidth = (chartRadius - arcMinRadius - numArcs * arcPadding) / numArcs;
let arc = d3.arc()
.innerRadius((d, i) => getInnerRadius(i))
.outerRadius((d, i) => getOuterRadius(i))
.startAngle(0)
.endAngle((d, i) => scale(d))
let radialAxis = svg.append('g')
.attr('class', 'r axis')
.selectAll('g')
.data(data)
.enter().append('g');
radialAxis.append('circle')
.attr('r', (d, i) => getOuterRadius(i) + arcPadding);
radialAxis.append('text')
.attr('x', labelPadding)
.attr('y', (d, i) => -getOuterRadius(i) + arcPadding)
.text(d => d.name);
let axialAxis = svg.append('g')
.attr('class', 'a axis')
.selectAll('g')
.data(ticks)
.enter().append('g')
.attr('transform', d => 'rotate(' + (rad2deg(scale(d)) - 90) + ')');
axialAxis.append('line')
.attr('x2', chartRadius);
axialAxis.append('text')
.attr('x', chartRadius + 10)
.style('text-anchor', d => (scale(d) >= PI && scale(d) < 2 * PI ? 'end' : null))
.attr('transform', d => 'rotate(' + (90 - rad2deg(scale(d))) + ',' + (chartRadius + 10) + ',0)')
.text(d => d);
//data arcs
let arcs = svg.append('g')
.attr('class', 'data')
.selectAll('path')
.data(data)
.enter().append('path')
.attr('class', 'arc')
.style('fill', (d, i) => color(i))
arcs.transition()
.delay((d, i) => i * 200)
.duration(1000)
.attrTween('d', arcTween);
arcs.on('mousemove', showTooltip)
arcs.on('mouseout', hideTooltip)
function arcTween(d, i) {
let interpolate = d3.interpolate(0, d.value);
return t => arc(interpolate(t), i);
}
function showTooltip(d) {
tooltip.style('left', (d3.event.pageX + 10) + 'px')
.style('top', (d3.event.pageY - 25) + 'px')
.style('display', 'inline-block')
.html(d.value);
}
function hideTooltip() {
tooltip.style('display', 'none');
}
function rad2deg(angle) {
return angle * 180 / PI;
}
function getInnerRadius(index) {
return arcMinRadius + (numArcs - (index + 1)) * (arcWidth + arcPadding);
}
function getOuterRadius(index) {
return getInnerRadius(index) + arcWidth;
}
});
</script>
</body>
</html>
@Dmitra
Copy link

Dmitra commented May 16, 2017

Nice job! Never seen this chart before.

Would you be so kind to add some extra touch of awesomeness by making the following:

  • use d3.v4
  • split html, js, css by files
  • rename title to reflect the data meaning (it's quite obvious that bl.ocks.org feature d3 charts), like: "Family electricity consumption"
  • animate bars from zero to value on page load
  • add touch of interactivity: faint highlight of bars on hover (just to be sure - that is not an image :)
  • show bar value on hover (find the right spot for it; my first guess is to try on the bar's top)
  • add a little bit of transparency to bars
  • fix inconsistent indentation
  • fix typo: "axcial"
  • fix inconsistent quotes usage (single are preferred)
  • center chart by container width (you've made a fake centering which fails if you open chart in separate window)
  • always use meaningful variable names: gr => "radialAxis" or even "radialAxisElement"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment