Skip to content

Instantly share code, notes, and snippets.

@pjsier
Last active July 24, 2019 06:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pjsier/205cbdeaed7be939beb0dcf37571f024 to your computer and use it in GitHub Desktop.
Save pjsier/205cbdeaed7be939beb0dcf37571f024 to your computer and use it in GitHub Desktop.
Horizontal Stacked Bar Chart
license: mit
<!DOCTYPE html>
<html>
<head>
<title>Diverging Stacked Bar Chart</title>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<meta charset='utf-8' />
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
#chart {
max-width: 600px;
max-height: 400px;
}
</style>
</head>
<body>
<button onclick="removeOne()">Remove</button><button onclick="addOne()">Add</button>
<div id="chart"></div>
<script src="script.js"></script>
<script>
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
var chart = divergingBarChart();
var data = [];
for (var i = 0; i < 3; ++i) {
data.push({ data: getRandomInt(1, 1000), label: "Test " + data.length });
}
function resize() {
if (d3.select("#chart svg").empty()) {
return;
}
var w = +d3.select("#chart").style("width").replace(/(px)/g, "");
chart.width(w);
chart.height(200);
d3.select("#chart").call(chart);
}
function removeOne() {
data.pop();
d3.select("#chart").datum(data).call(chart);
}
function addOne() {
data.push({ data: getRandomInt(1, 1000), label: "Test " + data.length });
d3.select("#chart").datum(data).call(chart);
}
document.addEventListener("DOMContentLoaded", function() {
d3.select("#chart").datum(data).call(chart);
d3.select(window).on('resize', resize);
});
</script>
</body>
</html>
function divergingBarChart() {
var margin = {top: 20, right: 20, bottom: 20, left: 20},
width = 600,
height = 150,
barHeight = 50,
padding = 10,
dataValue = function(d) { return +d.data; },
labelValue = function(d) { return d.label; },
color = ["#e24000", "#2c897f", "#434878"];
function chart(selection) {
selection.each(function(data) {
data = data.map(function(d) {
return { value: dataValue(d), label: labelValue(d) };
});
var sumVals = d3.sum(data, function(d) { return d.value; });
var barScale = d3.scaleLinear()
.domain([0, sumVals])
.range([0, (width-margin.left-margin.right)]);
var svg = d3.select(this).selectAll("svg").data([data]);
var gEnter = svg.enter().append("svg").append("g");
gEnter.append("g").attr("class", "rects")
.selectAll(".data-rects").data(data).enter()
.append("rect").attr("class", "data-rects");
gEnter.selectAll("line.legend")
.data(data).enter()
.append("line").attr("class", "legend");
gEnter.selectAll("text.legend")
.data(data).enter()
.append("text")
.attr("class", "legend")
.attr("font-size", 12);
var svg = selection.select("svg");
svg.attr('width', width).attr('height', height);
var g = svg.select("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var rectG = g.select("g.rects");
var dataRects = rectG.selectAll(".data-rects").data(data, function(d) { return d.label; });
dataRects.exit().remove();
dataRects.enter().append("rect").attr("class", "data-rects");
rectG.selectAll(".data-rects").transition()
.duration(750)
.attr("x", function(d, i) {
return data.slice(0, i).reduce(function (a, d) { return a + barScale(d.value); }, 1) + (padding / 2);
})
.attr("y", height-margin.bottom-barHeight)
.attr("width", function(d) { return Math.max(barScale(d.value) - (padding / 2), 1); })
.attr("height", barHeight)
.attr("fill", function(d, i) { return color[i]; });
// Getting midpoint for legend
function legendX(d, i) {
return data.slice(0, i).reduce(function (a, d) { return a + barScale(d.value); }, (Math.max((barScale(d.value) - (padding / 2)), 1) / 2)) + (padding / 2);
};
var legendLines = g.selectAll("line.legend").data(data, function(d) { return d.label; });
legendLines.exit().remove();
legendLines.enter().append("line").attr("class", "legend");
g.selectAll("line.legend").transition()
.duration(750)
.attr("x1", legendX)
.attr("x2", legendX)
.attr("y1", height-margin.bottom-barHeight-15)
.attr("y2", height-margin.bottom-barHeight-45)
.attr("stroke", "#000000")
.attr("stroke-width", 0.5);
var legendText = g.selectAll("text.legend").data(data, function(d) { return d.label; });
legendText.enter()
.append("text")
.attr("class", "legend")
.attr("font-size", 12);
legendText.exit().remove();
g.selectAll("text.legend").transition()
.duration(750)
.attr("text-anchor", "middle")
.attr("x", legendX)
.attr("y", height-margin.bottom-barHeight-55)
.text(function(d) { return d3.format(",")(d.value); });
});
}
chart.margin = function(_) {
if (!arguments.length) return margin;
margin = _;
return chart;
};
chart.width = function(_) {
if (!arguments.length) return width;
width = _;
return chart;
};
chart.height = function(_) {
if (!arguments.length) return height;
height = _;
return chart;
};
chart.dataValue = function(_) {
if (!arguments.length) return dataValue;
dataValue = _;
return chart;
};
chart.labelValue = function(_) {
if (!arguments.length) return labelValue;
labelValue = _;
return chart;
};
chart.color = function(_) {
if (!arguments.length) return color;
color = _;
return chart;
};
return chart;
}
@GayathriRamasamy
Copy link

Hi Patrick,

Can you give me a example of multiple rows of stacked column chart with dynamically adding stacks, while scrolling the new rows has to added, each row have multiple stacks ie)1000+ stacks and each stacks in the previous row are not matching to the current row ?

Thanks,
Gayathri

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