Skip to content

Instantly share code, notes, and snippets.

@Saigesp
Last active October 22, 2018 04:17
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 Saigesp/4ab7848d451694667830d91712759cc5 to your computer and use it in GitHub Desktop.
Save Saigesp/4ab7848d451694667830d91712759cc5 to your computer and use it in GitHub Desktop.
D3v4 Mean barchart
4ab7848d451694667830d91712759cc5

Mean barchart

D3 implementation of mean barchar

See the demo and more charts from d3graphs repository

Features:

  • Object oriented approach
  • Responsive
  • Resizable

Requires:

  • D3 v4+

Default options:

{
    margin: {top: 40, right: 30, bottom: 50, left: 40},
    key: 'key',
    label: 'date',
    colors: ['#95bd95', '#db8c8c'],
    greycolor: '#CCC',
    yscaleformat: '.0f',
    currentkey: false,
    title: false,
    source: false,
    mean: false,
    meanlabel: false,
}
class MeanBarChart{
constructor(selection, data, config = {}) {
let self = this;
this.selection = selection;
this.data = data;
// Graph configuration
this.cfg = {
margin: {top: 40, right: 30, bottom: 50, left: 40},
key: 'key',
label: 'date',
colors: ['#95bd95', '#db8c8c'],
greycolor: '#CCC',
yscaleformat: '.0f',
currentkey: false,
title: false,
source: false,
mean: false,
meanlabel: false,
};
Object.keys(config).forEach(function(key) {
if(config[key] instanceof Object && config[key] instanceof Array === false){
Object.keys(config[key]).forEach(function(sk) {
self.cfg[key][sk] = config[key][sk];
});
} else self.cfg[key] = config[key];
});
this.cfg.width = parseInt(this.selection.node().offsetWidth) - this.cfg.margin.left - this.cfg.margin.right,
this.cfg.height = parseInt(this.selection.node().offsetHeight)- this.cfg.margin.top - this.cfg.margin.bottom;
this.xScale = d3.scaleBand().rangeRound([0, this.cfg.width]).padding(0.1);
this.yScale = d3.scaleLinear().rangeRound([0, self.cfg.height]);
window.addEventListener("resize", function(){self.resize()});
this.initGraph();
}
initGraph() {
var self = this;
this.xScale.domain(this.data.map(function(d) { return d[self.cfg.label]; }));
this.yScale.domain([d3.max(this.data, function(d){ return +d[self.cfg.key]}),d3.min(this.data, function(d){ return +d[self.cfg.key]})])
this.svg = this.selection.append('svg')
.attr("class", "chart barchart")
//.attr("viewBox", "0 0 "+(this.cfg.width + this.cfg.margin.left + this.cfg.margin.right)+" "+(this.cfg.height + this.cfg.margin.top + this.cfg.margin.bottom))
.attr("width", this.cfg.width + this.cfg.margin.left + this.cfg.margin.right)
.attr("height", this.cfg.height + this.cfg.margin.top + this.cfg.margin.bottom);
this.g = this.svg.append("g")
.attr("transform", "translate(" + (self.cfg.margin.left) + "," + (self.cfg.margin.top) + ")");
// TITLE
if(self.cfg.title){
this.title = this.svg.append('text')
.attr('class', 'title label')
.attr('text-anchor', 'middle')
.attr('transform', 'translate('+ (self.cfg.width/2) +',20)')
.text(self.cfg.title)
}
// SOURCE
if(self.cfg.source){
this.source = this.svg.append('text')
.attr('class', 'source label')
.attr('transform', 'translate('+ (self.cfg.margin.left) +','+(self.cfg.height + self.cfg.margin.top + self.cfg.margin.bottom - 5)+')')
.html(self.cfg.source)
}
// GRID
this.yGrid = this.g.append("g")
.attr("class", "grid grid--y")
.call(self.make_y_gridlines()
.tickSize(-self.cfg.width)
.ticks(5, self.cfg.yscaleformat));
this.itemg = this.g.selectAll('.itemgroup')
.data(this.data)
.enter().append('g')
.attr('class', 'itemgroup')
.attr('transform', function(d, i){
return 'translate('+ self.xScale(d[self.cfg.label]) +',0)';
})
this.rects = this.itemg.append('rect')
.attr('x', 0)
.attr('y', function(d, i){
return +d[self.cfg.key] < 0 ? self.yScale(0) : self.yScale(+d[self.cfg.key]);
})
.attr('width', this.xScale.bandwidth())
.attr('height', function(d){
return +d[self.cfg.key] < 0 ? self.yScale(+d[self.cfg.key]) - self.yScale(0) : self.yScale(0) - self.yScale(+d[self.cfg.key]);
})
.attr('fill', function(d){
var c = +d[self.cfg.key] < 0 ? 1 : 0;
return !self.cfg.currentkey || d[self.cfg.key] == self.cfg.currentkey ? self.cfg.colors[c] : self.cfg.greycolor;
});
this.rects.append("title")
.text(function(d) { return d[self.cfg.key]});
// AXIS
this.xAxis = this.g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + this.yScale(0) + ")")
this.xAxisLine = this.xAxis.append('line')
.attr('stroke', 'black')
.attr('x1', 0)
.attr('y1', 0)
.attr('x2', this.cfg.width)
.attr('y2', 0)
this.xAxisLabels = this.xAxis.selectAll('text')
.data(this.data)
.enter().append('text')
.attr('class', 'label')
.attr('y', function(d){ return self.xScale(d[self.cfg.label]) + (self.xScale.bandwidth()/2) + 3 })
.attr('x', function(d){ return +d[self.cfg.key] < 0 ? 4 : -4 })
.attr('text-anchor', function(d){ return +d[self.cfg.key] < 0 ? 'start' : 'end'})
.text(function(d){ return d[self.cfg.label]})
.attr("transform", "rotate(-90)")
if(this.cfg.mean){
this.mean = this.g.append('line')
.attr('class', 'axis axis--mean')
.attr('stroke', 'black')
.attr('stroke-width', 1)
.attr('x1', 0)
.attr('y1', this.yScale(this.cfg.mean))
.attr('x2', this.cfg.width)
.attr('y2', this.yScale(this.cfg.mean))
if(this.cfg.meanlabel){
this.meanlabel = this.g.append('text')
.attr('class', 'label label--mean')
.attr('text-anchor', 'end')
.attr('x', this.cfg.width)
.attr('y', this.yScale(this.cfg.mean) - 3)
.text(this.cfg.meanlabel)
}
}
}
// gridlines in x axis function
make_x_gridlines() {
return d3.axisBottom(this.xScale);
}
// gridlines in y axis function
make_y_gridlines() {
return d3.axisLeft(this.yScale);
}
resize(){
var self = this;
this.cfg.width = parseInt(this.selection.node().offsetWidth) - this.cfg.margin.left - this.cfg.margin.right;
this.cfg.height = parseInt(this.selection.node().offsetHeight)- this.cfg.margin.top - this.cfg.margin.bottom;
this.xScale.rangeRound([0, this.cfg.width]);
this.yScale.rangeRound([0, this.cfg.height]);
this.svg
.attr("viewBox", "0 0 "+(this.cfg.width + this.cfg.margin.left + this.cfg.margin.right)+" "+(this.cfg.height + this.cfg.margin.top + this.cfg.margin.bottom))
.attr("width", this.cfg.width + this.cfg.margin.left + this.cfg.margin.right)
.attr("height", this.cfg.height + this.cfg.margin.top + this.cfg.margin.bottom);
this.yGrid
.call(self.make_y_gridlines()
.tickSize(-self.cfg.width)
.tickFormat(d3.format("d")));
this.xAxis.attr("transform", "translate(0," + this.yScale(0) + ")")
this.xAxisLine.attr('x2', this.cfg.width)
this.xAxisLabels
.attr('y', function(d){ return self.xScale(d[self.cfg.label]) + (self.xScale.bandwidth()/2) + 3 })
.attr('x', function(d){ return +d[self.cfg.key] < 0 ? 4 : -4 })
this.itemg.attr('transform', function(d, i){
return 'translate('+ self.xScale(d[self.cfg.label]) +',0)';
})
this.rects
.attr('width', this.xScale.bandwidth())
.attr('y', function(d, i){
return self.yScale(+d[self.cfg.key]);
})
.attr('height', function(d){
return self.cfg.height - self.yScale(+d[self.cfg.key]);
})
this.rects.attr('y', function(d, i){
return +d[self.cfg.key] < 0 ? self.yScale(0) : self.yScale(+d[self.cfg.key]);
})
.attr('height', function(d){
return +d[self.cfg.key] < 0 ? self.yScale(+d[self.cfg.key]) - self.yScale(0) : self.yScale(0) - self.yScale(+d[self.cfg.key]);
});
if(this.cfg.mean){
this.mean.attr('y1', this.yScale(self.cfg.mean))
.attr('x2', self.cfg.width)
.attr('y2', self.yScale(self.cfg.mean))
if(this.cfg.meanlabel) this.meanlabel.attr('x', this.cfg.width).attr('y', this.yScale(this.cfg.mean) - 3)
}
if(self.cfg.title) this.title.attr('transform', 'translate('+ (self.cfg.width/2) +',20)')
if(self.cfg.source) this.source.attr('transform', 'translate('+ (self.cfg.margin.left) +','+(self.cfg.height + self.cfg.margin.top + self.cfg.margin.bottom - 5)+')')
}
}
year ton
1982 -2650
1983 -315
1984 -1790
1985 -3963
1986 -4018
1987 -16941
1988 -27276
1989 -30078
1990 -10059
1991 712
1992 2748
1993 5526
1994 8574
1995 15805
1996 14582
1997 26694
1998 35205
1999 45944
2000 78595
2001 71530
2002 45407
2003 54044
2004 69165
2005 61798
2006 63686
2007 76973
2008 47325
2009 35800
2010 24981
2011 10783
2012 7548
2013 -595
2014 -50520
2015 -10198
2016 -23290
2017 -16958
<html>
<head>
<meta charset="utf-8">
</head>
<style>
.chart .grid line {
opacity: 0.1;
}
.chart .grid path {
fill: transparent;
stroke: transparent;
}
.label {
font-family: sans-serif;
font-size: 10px;
cursor: default;
}
.chart .source {
fill: #7a7a7a;
font-size: 10px;
}
.chart .source a {
text-decoration: underline;
}
.chart .title {
font-weight: 700;
}
/* BARCHART */
.chart .axis {
shape-rendering: crispEdges;
}
</style>
<body>
<div id="chart" style="width: 100%; height: 360px;"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<script src="d3.meanbar.js"></script>
<script>
d3.csv('data.csv', function(data) {
var grafico = new MeanBarChart(d3.select('#chart'), data, {
'key': 'ton',
'label': 'year',
'mean': 23000,
'meanlabel': 'Predefined mean',
'color': '#DD8500',
'yscaleformat': '.0s',
'title': 'Random title',
'source': 'Source: Lorem ipsum',
})
})
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment