Skip to content

Instantly share code, notes, and snippets.

@erikhazzard
Created May 2, 2013 20: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 erikhazzard/5505085 to your computer and use it in GitHub Desktop.
Save erikhazzard/5505085 to your computer and use it in GitHub Desktop.
stacked bars updating set width abstract negative positive
{"description":"stacked bars updating set width abstract negative positive","endpoint":"","display":"svg","public":true,"require":[],"fileconfigs":{"inlet.js":{"default":true,"vim":false,"emacs":false,"fontSize":12},"_.md":{"default":true,"vim":false,"emacs":false,"fontSize":12},"config.json":{"default":true,"vim":false,"emacs":false,"fontSize":12}},"fullscreen":false,"play":false,"loop":false,"restart":false,"autoinit":true,"pause":true,"loop_type":"period","bv":false,"nclones":15,"clone_opacity":0.4,"duration":3000,"ease":"linear","dt":0.01}
//---------------------------------------
//Some config vars
//---------------------------------------
var DURATION = 1500;
var width = 890,
height = 460;
//---------------------------------------
//Setup svg
//---------------------------------------
var svg = d3.select('svg')
.attr('width', width)
.attr('height', height)
.append('g');
//---------------------------------------
//setup total bar width / height scale
//---------------------------------------
var totalXScale = d3.scale.linear()
.domain([0, 1000])
.rangeRound([0,width]);
var totalYScale = d3.scale.linear()
.domain([0, 1000])
.rangeRound([0, height - 10])
.clamp(true);
//---------------------------------------
//Color config
//---------------------------------------
var colorConfig = {
positive: ['#aed8ca', '#6dbaac'],
negative: ['#efc5b0', '#e37e4c']
};
var yPadding = 10;
var xPadding = 5;
//=======================================
//
//Setup stack objects
//
//=======================================
//Base stack object
var StackObject = function(options){
options = options || {};
//default options
this.width = 0;
this.height = 0;
this.sign = 'positive'; // or 'negative'
//extend / override properties based on passed in options
_.extend(this, options);
return this;
};
//position / size functions
StackObject.prototype.getHeight = function(){
return totalYScale(this.height);
};
StackObject.prototype.getY = function(){
return 0;
};
StackObject.prototype.getWidth = function(){
return totalXScale(this.width);
};
StackObject.prototype.getX = function(){
return 0;
};
StackObject.prototype.color = function(){
return d3.scale.linear()
.clamp(true)
//TODO: use data max
.domain([0, 1000])
.range(colorConfig[this.sign]);
};
//---------------------------------------
//TAXES
//---------------------------------------
var stackObjectTaxesPositive = new StackObject({
group: svg.append('svg:g').attr({
'class': 'group1'
}),
height: 400,
width: 400
});
//---------------------------------------
//SPENDING
//---------------------------------------
var stackObjectSpendingPositive = new StackObject({
group: svg.append('svg:g').attr({
'class': 'group2',
transform: 'translate(0, 205)'
}),
height: 600,
getY: function(){
//TODO: Get data from tax height
return stackObjectTaxesPositive.getHeight() + yPadding;
},
//width
width: 300
});
var stackObjectSpendingNegative = new StackObject({
group: svg.append('svg:g').attr({
'class': 'group2',
transform: 'translate(0, 205)'
}),
sign: 'negative',
height: 600,
getY: function(){
//TODO: Get data from tax height
return stackObjectTaxesPositive.getHeight() + yPadding;
},
getX: function(){
//TODO: Get data from tax height
return stackObjectSpendingPositive.getWidth() + xPadding;
},
//width
width: 400
});
//=======================================
//
//Update / Draw stacked bar
//
//=======================================
function update(stackObject, data) {
//Draws or updates the stacked bars
// parameters:
// stackObject: {Object} containing properties of the stack bar
// data: {Object} data for the stacked bar
//
//NOTE: y0 is the previous y value, y1 is the current data value
//setup scales
var yScale = d3.scale.linear()
.rangeRound([stackObject.getWidth(), 0]);
var color = stackObject.color();
var stackGroup = stackObject.group;
//Setup stacked data
data.forEach(function(d) {
var y0 = 0;
d.policies = d3.keys(data[0]).map(function(name) {
return {
name: name,
y0: y0,
y1: y0 += +d[name]
};
});
d.total = d.policies[d.policies.length - 1].y1;
});
data.sort(function(a, b) { return b.total - a.total; });
//Setup scale domains
yScale.domain([0, d3.max(data, function(d) { return d.total; })]);
//get height of the bars (based on spending - taxes)
var barHeight = stackObject.getHeight();
//update y position of group
stackObject.group.transition().duration(DURATION).attr({
transform: 'translate(' + [
stackObject.getX(),
stackObject.getY()
] + ')'
});
// DATA JOIN
// Join new data with old elements, if any.
var policy = stackGroup.selectAll('.policy')
.data(data[0].policies);
//flip it
//stackGroup.attr({ transform: 'translate(' + [width, 0] + ') scale(-1 1)' });
//enter
policy.enter().append('rect').attr({
'class': 'policy',
'y': 0,
'height': barHeight
}).style({
opacity: 1,
stroke: '#ffffff'
}).on('mouseover', function(d,i){
d3.select(this).style({
fill: d3.rgb(color(d.y1)).hsl().brighter(0.7)
});
}).on('mouseout', function(d,i){
d3.select(this).style({
fill: color(d.y1)
});
});
//update
policy.style({
opacity: 1,
'fill': function(d,i) { return color(d.y1); }
})
.transition()
.duration(DURATION)
.attr({
'height': barHeight,
'x': function(d) { return yScale(d.y1); },
'width': function(d) { return yScale(d.y0) - yScale(d.y1); }
});
//EXIT
policy.exit()
.transition()
.duration(DURATION)
.attr({
'height': 0,
'width': function(d) {
return 0;
},
'x': function(d) {
return 0;
}
}).style({
opacity: 0.1
})
.remove();
}
// The initial display.
update(stackObjectTaxesPositive,
[{
a: 310504,
b: 552339,
d: 259034,
e: 450818,
f: 1231572,
g: 1215966
}]
);
update(stackObjectSpendingPositive,
[{
a: 310504,
b: 552339,
d: 259034,
e: 450818,
f: 1231572,
g: 1215966
}]
);
update(stackObjectSpendingNegative,
[{
a: 310504,
b: 552339,
d: 259034,
e: 450818,
f: 1231572,
g: 1215966
}]
);
//Draw and update two bars
setInterval(function(){
var num = 1000 - Math.random() * 1000 | 0;
stackObjectTaxesPositive.height = num;
stackObjectSpendingPositive.height = (1000 - num);
stackObjectSpendingNegative.height = (1000 - num);
var updateBar = function(stack){
var data = [{}];
var alphabet = 'abcdefghijklmnopqrstuvwxyz';
for(i=0; i < Math.random() * 26 | 0;i++){
data[0][alphabet[i]] = Math.random() * 1000 | 0;
}
update(
stack,
_.clone(data)
);
};
updateBar(stackObjectSpendingPositive);
updateBar(stackObjectSpendingNegative);
updateBar(stackObjectTaxesPositive);
}, DURATION + 300);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment