Built with blockbuilder.org
xxxxxxxxxx
<html lang="en">
<head>
<meta charset="utf-8">
<title>stacked bar chart</title>
<style>
* {
font-family: sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #888;
shape-rendering: crispEdges;
}
.axis text {
font-size: 10px;
fill: #222;
}
.y.axis line {
stroke: #bbb;
opacity: 0.7;
}
.y.axis path {
stroke-width: 0;
}
</style>
</head>
<body>
<div class="chart"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script>
//define layout vars
var width = 800,
height = 200,
margin = {
top: 100,
right: 10,
bottom: 200,
left: 50
}
//define range for x/y scales
var x = d3.scale.ordinal().rangeRoundBands([0, width], 0.2);
var y = d3.scale.linear().rangeRound([height, 0]);
//define color scale
var colors = d3.scale.ordinal().range(['#a93790', '#ec6e8d', '#f9c7a8']);
//define x/y axes
var xAxis = d3.svg.axis().scale(x).orient('bottom');
var yAxis = d3.svg.axis().scale(y).orient('left').ticks(5);
//define svg
var svg = d3.select('.chart').append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')');
//load data
d3.csv('data.csv', function(err, data) {
if (err) throw err;
//get the keys for x scale
keys = d3.keys(data[0]).filter(function(d) { return d != 'Quarter'; });
//define the domain for colors scale
colors.domain(keys);
//parse data
data.forEach(function(d) {
d.Mac = parseFloat(d.Mac);
d.iPad = parseFloat(d.iPad);
d.iPhone = parseFloat(d.iPhone);
var y0 = 0;
d.values = keys.map(function(key) {
return {
key: key,
quarter: d.Quarter,
y0: y0,
y1: y0 += +d[key]
};
});
});
//define the domain for x/y scale
x.domain(data.map(function(d) { return d.Quarter; }));
y.domain([0, d3.max(data, function(d) {
return Math.round((d.Mac + d.iPad + d.iPhone) * 1.35);
})]);
//append a group for each quarter
var quarters = svg.selectAll('g.quarter')
.data(data)
.enter().append('g')
.attr('class', 'quarter');
//plot bars for each series
quarters.selectAll('rect.bar')
.data(function(d) { return d.values; })
.enter().append('rect')
.attr('class', function(d) { return 'bar ' + d.key; })
.attr('name', function(d) { return d.key; })
.attr('cursor', 'pointer')
.attr('x', function(d) { return x(d.quarter); })
.attr('y', function(d) { return y(d.y1); })
.attr('y0', function(d) { return d.y0; })
.attr('width', x.rangeBand())
.attr('height', function(d) { return y(d.y0) - y(d.y1); })
.attr('fill', function(d) { return colors(d.key); });
//draw x/y axes
svg.append('g')
.attr('class', 'x axis')
.attr('pointer-events', 'none')
.attr('transform', 'translate(0, ' + height + ')')
.call(xAxis);
svg.append('g')
.attr('class', 'y axis')
.call(yAxis)
.append('text')
.attr('x', -26)
.attr('y', 0)
.text('Unit: million');
//draw legend groups
var legends = svg.selectAll('g.legend')
.data(keys)
.enter().append('g')
.attr('class', 'legend')
.attr('transform', function(d, i) {
return 'translate(0, ' + ((keys.length - 1 - i) * 18 - 10) + ')';
});
//draw legend rects
legends.append('rect')
.attr('class', function(d) {
return 'bar ' + d;
})
.attr('name', function(d) { return d; })
.attr('cursor', 'pointer')
.attr('x', width - 44)
.attr('y', -10)
.attr('width', 34)
.attr('height', 15)
.style('fill', function(d) {
return colors(d);
});
//draw legend labels
legends.append('text')
.attr('x', width - 28)
.attr('y', 1)
.attr('pointer-events', 'none')
.style('text-anchor', 'middle')
.style('font-size', 10)
.style('fill', '#fff')
.text(function(d) { return d; });
//draw legend indicator
var indicator = svg.append('text')
.attr('class', 'legend-indicator')
.attr('x', width - 96)
.attr('y', -8)
.style('font-size', 10)
.style('opacity', 0)
.text('comparing');
//draw title
svg.append('text')
.attr('x', (width + margin.left + margin.right) / 2)
.attr('y', -20)
.style('text-anchor', 'middle')
.style('font-size', 20)
.text('Apple product unit sales');
//draw note
svg.append('a')
.attr('href', 'https://barefigur.es/companies/apple/products/')
.append('text')
.attr('x', -16)
.attr('y', height + 60)
.style('font-size', 12)
.text('Data source: Bare Figures')
//add user interaction
d3.selectAll('.bar').on('click', function() {
//vertically translate bars for comparison
var selected = d3.select(this).attr('name');
quarters.transition()
.duration(200)
.attr('transform', function(d) {
var y0 = d.values.filter(function(k) { return k.key === selected; })[0].y0;
return 'translate(0, ' + (height - y(y0)) + ')';
});
//conditionally style x axis label color
if (selected === keys[0]) {
d3.selectAll('.x.axis text')
.transition()
.duration(200)
.style('fill', '#222');
} else {
d3.selectAll('.x.axis text')
.transition()
.duration(200)
.style('fill', '#fff');
}
//show legend indicator
indicator.style('opacity', 1);
//position legend indicator
var index = keys.indexOf(selected);
indicator.transition()
.duration(200)
.attr('transform', 'translate(0, ' + (keys.length - 1 - index) * 18 + ')')
});
d3.selectAll('.bar').on('mouseover', function() {
//change opacity of selected series
var selected = d3.select(this).attr('name');
d3.selectAll('.bar.' + selected).style('fill-opacity', 0.75);
});
d3.selectAll('.bar').on('mouseout', function() {
//change opacity of selected series
var selected = d3.select(this).attr('name');
d3.selectAll('.bar.' + selected).style('fill-opacity', 1);
});
});
</script>
</body>
</html>
https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js