Public
Edited
Feb 28, 2023
1 fork
4 stars
Insert cell
Insert cell
Insert cell
capsuleBar(data.data, {
barCount: barValue, // how much each capsule represents e.g. 100
pairFill: [col_1, col_2] // colours passed in as an array
})
Insert cell
Insert cell
function capsuleBar (_data, {
barCount = 100, // amount each capsule/bar should represent
roundTo = 100, // number to round the capsule then
label = 'label', // key for label
value_one = 'female', // keys to take data from that match the sets above
value_two = 'male', // keys to take data from that match the sets above
labelKey = 'label', // key for label e.g. 2022
pairFill = ['#0b3536', '#0098d8'], // colours for each pair
stroke_width = 10, // width of bar (they are lines/strokes)
barWidth = 50, // width of capusle
barPadding = 2.6, // spacing between bars
margin = {left: 70, top: 20, right: 30, bottom: 40} // chart margins
} = {}) {

const data = _data.map(d => {
const obj = {...d};
// have a total value of capsules/bars
obj.total = barCountNum(d[value_one] + d[value_two], roundTo);
// have the count of where the bars change colour
obj.value_one_bars = barCountNum(d[value_one], roundTo);
return obj
})

const max = d3.max(data.map(d => d.total))
const height = max * (stroke_width * 1.4);
const width = (barWidth * barPadding) * data.length;

const w = width + (margin.left + margin.right);
const h = height + (margin.top + margin.bottom);
const yScale = d3.scaleLinear()
.domain([0, max])
.range([height, margin.top]);

const xScale = d3.scaleLinear()
.domain([0, data.length])
.range([margin.left, w])
const svg = DOM.svg(w, h);
const sel = d3.select(svg);

// group for each segment
const join = sel.selectAll('g')
.data(data)
.join('g')
.attr('transform', (d, i) => {
return `translate(${xScale(i)}, ${margin.top})`
});

join.selectAll('line')
.data(d => d3.range(d.total).map(() => d.value_one_bars))
.join('line')
.attr('y1', (d, i) => yScale(i))
.attr('y2', (d, i) => yScale(i))
.attr('x1', 0)
.attr('x2', barWidth)
.attr('stroke-width', stroke_width)
.attr('stroke-linecap', 'round')
.attr('stroke', (value_one_bars, i) => {
// value_one_bars has the count where the bars change colour
if ((i + 1) <= value_one_bars) {
return pairFill[0]
} else {
return pairFill[1]
}
})

// labels for columns
join.append('text')
.attr('x', barWidth/2)
.attr('y', height + margin.top + 8)
.attr('font-size', '11px')
.attr('text-anchor', 'middle')
.attr('fill', '#454545')
.text(d => d[label])

// pct line
join.append('line')
.attr('y1', d => yScale(d.value_one_bars - 1) - stroke_width/2 -1.5)
.attr('y2', d => yScale(d.value_one_bars - 1) - stroke_width/2 -1.5)
.attr('x1', -15)
.attr('x2', barWidth + 5)
.attr('stroke', pairFill[0])
.attr('stroke-dasharray', '1 1')
.attr('stroke-width', '1px')

// pct labels
join.append('text')
.attr('x', -20)
.attr('y', d => yScale(d.value_one_bars - 1) - 3)
.attr('font-size', '14px')
.attr('text-anchor', 'end')
.attr('fill', pairFill[0])
.text(d => calcPctVal1(d[value_one], d[value_two]) + '%')

// have the total number of bars after rounding
function barCountNum (value) {
return roundNearestX(value) / barCount;
}

function roundNearestX (num, x = 100) {
return Math.round(num / x) * x;
}

function calcPctVal1 (val1, val2) {
const total = val1 + val2
return Math.round((val1 / total) * 100);
}
return svg;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
data = { return {
label: 'Group A',
// capusleDivision: 100, // amount each capsule should be
data: [ // send the data into chart ordered largest population to lowest (sorted by the accumulative value of the values array)
{
label: 'Team A',
female: 400,
male: 800
},
{
label: 'Team B',
female: 550,
male: 876
},
{
label: 'Team C',
female: 233,
male: 1345
},
{
label: 'Team D',
female: 890,
male: 2456
}
].reverse()
}
};
Insert cell
<!-- css not required for chart -->
<hr>
<link href="https://fonts.googleapis.com/css?family=Space+Mono" rel="stylesheet">
<style>
text {
font-family:'Space Mono',monospace;
}
</style>
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