xxxxxxxxxx
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
</style>
</head>
<body>
<script>
const data = [
{
step: 'Mars',
complexity: 10,
task: 'Task 1',
},
{
step: 'Jupiter',
complexity: 60,
task: 'Task 1',
},
{
step: 'Neptune',
complexity: 10,
task: 'Task 1',
},
{
step: 'Venus',
complexity: 25,
task: 'Task 1',
},
{
step: 'Mars',
complexity: 30,
task: 'Task 2',
},
{
step: 'Jupiter',
complexity: 10,
task: 'Task 2',
},
{
step: 'Neptune',
complexity: 50,
task: 'Task 2',
},
{
step: 'Mars',
complexity: 20,
task: 'Task 3',
},
{
step: 'Jupiter',
complexity: 40,
task: 'Task 3',
},
{
step: 'Mars',
complexity: 45,
task: 'Task 4',
},
{
step: 'Jupiter',
complexity: 60,
task: 'Task 4',
},
{
step: 'Mars',
complexity: 35,
task: 'Task 5',
},
{
step: 'Jupiter',
complexity: 30,
task: 'Task 5',
},
{
step: 'Neptune',
complexity: 50,
task: 'Task 5',
},
];
//================================================================
// Prep work
//================================================================
const svg = d3
.select('body')
.append('svg')
.attr('width', 600)
.attr('height', 180);
const margin = {top: 20, right: 20, bottom: 30, left: 40};
const width = +svg.attr('width') - margin.left - margin.right;
const height = +svg.attr('height') - margin.top - margin.bottom;
const chartContainer = svg
.append('g')
.attr('transform', `translate(${margin.left}, ${margin.top})`)
.attr('class', 'chart');
// Create an array with this structures:
// [{
// key: "Task 1",
// values: [{step: "Mars", complexity: 10, task: "Task 1"} ...]
// },{
// key: "Task 2",
// values: []
// }
// ...]
const groups = d3.nest()
.key(d => d.task)
.entries(data);
//================================================================
// Scales
//================================================================
const xGroupScale = d3
.scaleBand()
.domain(groups.map(d => d.key))
.rangeRound([0, width])
.padding(0.1);
const xBarScale = d3
.scaleBand()
.domain(data.map((data) => data.step))
.rangeRound([0, xGroupScale.bandwidth()])
.padding(0.2);
const yScale = d3
.scaleLinear()
.domain([0, d3.max(data, (data) => data.complexity)])
.rangeRound([height, 0]);
const colorScale = d3
.scaleOrdinal()
.range([
'rgb(32, 213, 210)',
'rgb(169, 112, 255)',
'rgb(0, 88, 161)',
'rgb(108, 202, 255)',
'rgb(238, 83, 139)',
'rgb(0, 97, 97)',
'rgb(216, 216, 216)',
]);
//================================================================
// Bars
//================================================================
const barGroup = chartContainer
.append('g')
.attr('class', 'bar-groups')
.selectAll('g')
.data(groups)
.enter()
.append('g')
.attr('class', (data) => `${data.key}`)
.attr('transform', (data) => `translate(${xGroupScale(data.key)}, 0)`);
// .data(d => d.values) passes the array of data for each nested tasks
// on to the nested selection of 'rect's.
const bar = barGroup
.selectAll('rect')
.data(d => d.values)
.enter()
.append('rect')
.attr('class', 'bar')
.attr('x', (data) => xBarScale(data.step))
.attr('y', (data) => yScale(data.complexity))
.attr('width', xBarScale.bandwidth())
.attr('height', (data) => height - yScale(data.complexity))
.style('fill', (data) => colorScale(data.step));
//================================================================
// Axis
//================================================================
chartContainer
.append('g')
.attr('class', 'axis axis--x')
.attr('transform', `translate(0, ${height})`)
.call(d3.axisBottom(xGroupScale));
chartContainer
.append('g')
.attr('class', 'axis axis--y')
.call(d3.axisLeft(yScale).ticks(8));
chartContainer
.selectAll('.axis')
.selectAll('path')
.style('stroke', 'none');
</script>
</body>
https://d3js.org/d3.v4.min.js