[ Launch: Quintiles Stacked ] 6136753 by enoex
-
-
Save erikhazzard/6136753 to your computer and use it in GitHub Desktop.
Quintiles Stacked
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{"description":"Quintiles Stacked","endpoint":"","display":"svg","public":true,"require":[],"fileconfigs":{"inlet.js":{"default":true,"vim":false,"emacs":false,"fontSize":12},"style.css":{"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,"thumbnail":"http://i.imgur.com/cZXCdLP.png"} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// --------------------------------------------------------------------------- | |
// Negative / Positive Stacked bar chart | |
// --------------------------------------------------------------------------- | |
// | |
var getData = function getData(){ | |
// returns some dummy data | |
// *note*: In this example, the data is expected to be in the format of | |
// an array with sub arrays. Each sub array represents a group of stacked | |
// bars, each item in each sub array is an object representing an individual | |
// "stacked" item | |
// | |
// You can change the StackedChart function to work with your data format by | |
// modifying the `setupStack` function. | |
var dummyData = []; | |
var stack = null; | |
// Make some random data | |
// (*note* use Math.ceil so 0 is never generated) | |
var rand = Math.ceil(Math.random() * 10); | |
for(var i=0; i < 5; i++){ | |
stack = []; | |
for(var j=0; j < Math.ceil(Math.random() * 10); j++){ | |
stack.push({ | |
value: Math.round(-100 + Math.random() * 200) + 1, | |
name: i + '-' + j, | |
group: 'Group ' + i | |
}); | |
} | |
dummyData.push(stack); | |
} | |
//// To see structure of data: | |
// console.log( JSON.stringify( dummyData, null, 4 ) ); | |
return dummyData; | |
}; | |
// --------------------------------------------------------------------------- | |
// Stacked Chart | |
// --------------------------------------------------------------------------- | |
var StackedChart = function StackedChart(config){ | |
// Config | |
// -------------------------------------- | |
// default values | |
config = config || {}; | |
var width = config.width || 700; | |
var height = config.height || 500; | |
var margin = config.margin || { | |
top: 10, right: 10, | |
bottom: 10, left: 40 | |
}; | |
var data = config.data || []; | |
var duration = config.duration || 700; | |
// color scale | |
var color; | |
// assumes an SVG element already exists | |
// (note: attributes will be reset in chart()) | |
var svg = d3.select('svg').attr({ | |
width: width, | |
height: height | |
}); | |
// keep track of first draw, used so the axes don't transition | |
var isFirstDraw = true; | |
var chartLeftMargin = 10; | |
// Setup groups | |
// ---------------------------------- | |
var groups = {}; | |
groups.chart = svg.append('g').attr({ 'class': 'chart' }); | |
// Axes | |
groups.axes = groups.chart.append('g').attr({ 'class': 'axes' }); | |
groups.xAxis = groups.axes.append('g') | |
.attr({'class': 'axis x'}); | |
groups.yAxis = groups.axes.append('g') | |
.attr({'class': 'axis y'}); | |
// Chart | |
groups.stackChartWrapper = groups.chart.append('g') | |
.attr({ | |
'class': 'stackChartWrapper', | |
transform: 'translate(' + [chartLeftMargin, 0] + ')' | |
}); | |
// add group for zero line | |
groups.zeroLine = groups.chart.append('g').attr({ 'class': 'zeroLine' }); | |
// Annotations | |
groups.annotations = groups.chart.append('g') | |
.attr({ 'class': 'annotations' }); | |
// add a text element (will be updated during interactions) | |
var annotationText = groups.annotations.append('svg:text') | |
.attr({ | |
'class': 'annotationText', | |
x: 0, | |
y: 0 | |
}).text(''); | |
// Styles | |
// ---------------------------------- | |
var styles = { | |
stackRect: { | |
fill: '#f4f4f4', | |
stroke: '#f4f4f4' | |
}, | |
colors: { | |
positive: '#aed8ca', | |
negative: '#efc5b0' | |
} | |
}; | |
// -------------------------------------- | |
// Setup scales and axes | |
// -------------------------------------- | |
var xScale; | |
var yScale; | |
var bandPadding = 0.1; | |
var updateScales = function updateScales(){ | |
// setup an ordinal scale for the x axis. The input domain will be an | |
// array of group names (from the data) | |
xScale = d3.scale.ordinal().domain(data.map(function(datum,i){ | |
// We'll always have at least element in the datum array | |
return datum[0].group; | |
})) | |
.rangeRoundBands([margin.left, width - (margin.left + margin.right)], bandPadding); | |
// Setup a linear scale for the y axis | |
var merged = d3.merge(data); | |
yScale = d3.scale.linear().domain([ | |
// the min data should be the base y minus the size | |
d3.min(merged, function(d){ return d.y0-d.size; }), | |
// y0 will contain the highest value | |
d3.max(merged, function(d){ return d.y0; }) | |
]) | |
.range([height - (margin.top + margin.bottom), margin.bottom]) | |
// nice it so we get nice round values | |
.nice(); | |
// Update color scale | |
var yDomain = yScale.domain(); | |
color = d3.scale.linear() | |
.domain([yDomain[0], 0, yDomain[1]]) | |
.range(['#d7301f', '#fee8c8', '#7ce07d']); | |
// store value of the zero bar position so we don't have to recalculate | |
helpers.y0 = yScale(0); | |
}; | |
// Setup Axes | |
// -------------------------------------- | |
var updateAxis = function(){ | |
// Update the x and y axis | |
var yAxis = d3.svg.axis() | |
.tickSize(-width - (margin.left + margin.right)) | |
.scale(yScale) | |
.ticks(6) | |
.orient("left"); | |
var axesDuration = duration; | |
if(isFirstDraw){ axesDuration = 0; } | |
// use the axes group defined above | |
groups.yAxis | |
.transition() | |
.duration(axesDuration) | |
.attr({ | |
transform: "translate (" + [ xScale(margin.left), 0 ] + ")" | |
}) | |
.call(yAxis); | |
// Draw a zero line | |
var zeroLine = groups.zeroLine.selectAll('.zeroLine') | |
.data([{}]); | |
zeroLine.enter().append('svg:line') | |
.attr({ | |
'class': 'zeroLine' | |
}); | |
zeroLine.attr({ | |
y1: helpers.y0, | |
y2: helpers.y0, | |
x1: xScale(margin.left), | |
x2: width - (margin.left + margin.right) + xScale(margin.left) | |
}); | |
}; | |
// ----------------------------------------------------------------------- | |
// Update Chart | |
// ----------------------------------------------------------------------- | |
var updateStackGroups = function(){ | |
// setup a group for each stack | |
// ------------------------------ | |
var stackGroups = groups.stackChartWrapper.selectAll('.stackWrapper') | |
.data(data); | |
groups.stackGroups = stackGroups; | |
// ** Enter ** stacked groups | |
stackGroups.enter().append('svg:g') | |
.attr({ | |
'class': function(d,i){ | |
return 'stackWrapper group' + i; | |
} | |
}) | |
// -------------------------- | |
// ** INTERACTION ** | |
// -------------------------- | |
.on('mouseenter', function(d,i){ | |
var $sel = d3.select(this); | |
//// Fade out all other bars | |
//groups.stackChartWrapper.selectAll('.stackWrapper') | |
//.style({ opacity: 0.5 }); | |
// fade out labels | |
groups.axes.selectAll('text') | |
.transition().duration(duration/2) | |
.style({ opacity: 0.08 }); | |
$sel.style({ opacity: 1 }); | |
// Hide the net rect for this item | |
// NET rect | |
var netRect = $sel.select('.netRect'); | |
$sel.select('.netRect') | |
.transition().duration(duration) | |
.style({ opacity: 0 }) | |
// Make net bar as big as outline bar | |
// (NOTE : Gets reset to normal y / height on mouseleave | |
.attr({ | |
y: helpers.stackY, | |
height: helpers.stackHeight | |
}); | |
// Show the individual stack rects | |
// STACKS | |
$sel.selectAll('.stackRectsGroup') | |
.transition().duration(duration) | |
.style({ opacity: 1 }); | |
// Update position of each stack item | |
$sel.selectAll('.stackRect') | |
.transition().duration(duration) | |
.attr({ | |
y: function(d,i){ return yScale(d.y0); }, | |
height: function(d,i){ return yScale(0) - yScale(d.size); } | |
}) | |
.style({ opacity: 1 }); | |
// Show net circle | |
$sel.selectAll('.netDot') | |
.style({ opacity: 1 }); | |
// Draw line from net bar to axis | |
$sel.selectAll('.netLine') | |
.attr({ | |
x1: function(){ | |
// make the line extend across axis on left side | |
// a bit | |
return helpers.yAxisLocation() - 9; }, | |
x2: helpers.netMidX, | |
opacity: 1 | |
}); | |
// Update annotation | |
annotationText.attr({ | |
x: function(){ return helpers.yAxisLocation(); }, | |
y: function(){ return helpers.netSumY(d,i) + 4; } | |
}).text( function(){ return helpers.getGroupSum(d); }); | |
}) | |
// MOUSE LEAVE | |
.on('mouseleave', function(d,i){ | |
var $sel = d3.select(this); | |
//// Reset everything to normal | |
//groups.stackChartWrapper.selectAll('.stackWrapper') | |
//.style({ opacity: 1 }); | |
groups.axes.selectAll('text') | |
.transition().duration(duration/2) | |
.style({ opacity: 1 }); | |
stackGroups.selectAll('.outlineRect') | |
.style({ opacity: 1 }); | |
// Hide the net rect for this item | |
// NET rect | |
var netRect = $sel.select('.netRect'); | |
netRect.transition().duration(duration) | |
.attr({ | |
y: helpers.netY, | |
height: helpers.netHeight | |
}) | |
.style({ opacity: 1 }); | |
// Shrink down the individual stack rects | |
$sel.selectAll('.stackRect') | |
.transition().duration(duration) | |
.style({ opacity: 0 }); | |
//// NOTE: if we want stack items to come from center, | |
//set their properties like this: | |
//.attr({ | |
//y: parseInt(netRect.attr('y'),10) + | |
//(parseInt(netRect.attr('height'),10) / 2), | |
//height: 0 | |
//}); | |
// reset net line | |
$sel.selectAll('.netLine') | |
.attr({ | |
x1: helpers.netMidX, | |
x2: helpers.netMidX, | |
opacity: 1 | |
}); | |
$sel.selectAll('.netDot') | |
.style({ opacity: 0 }); | |
}); | |
// ** Exit ** stacked groups | |
stackGroups.exit().transition().duration(duration) | |
.style({ opacity: 0 }) | |
.remove(); | |
}; | |
// Invisible Interaction Rects | |
// ---------------------------------- | |
var updateInteractionRects = function(){ | |
// Creates and updates rects placed behind everything | |
// to aid interaction | |
var stackGroups = groups.stackGroups; | |
// Setup Invisible interaction rect for each group | |
// ------------------------------ | |
var interactionRects = stackGroups.selectAll('.interactionRect') | |
.data(function(d, i){ | |
// Wrap each array of group items in an array so only | |
// one item is added below for each rect | |
return [d]; | |
}); | |
// ** Enter ** interaction bars | |
var interactionPadding = 35; | |
interactionRects.enter().append('rect') | |
.attr({ | |
'class': function(d,i){ | |
return 'interactionRect interaction' + i; | |
}, | |
x: helpers.stackX, | |
y: helpers.y0, | |
height: 0, | |
width: xScale.rangeBand() | |
}).style({ opacity: 0 }); | |
// ** Update ** | |
// Add some padding so there's a larger interaciton radius | |
interactionRects.attr({ | |
y: function(d,i){ | |
return helpers.stackY(d,i) - interactionPadding; | |
}, | |
height: function(d,i){ | |
return helpers.stackHeight(d,i) + (interactionPadding * 2); | |
}, | |
x: function(d,i){ | |
return helpers.stackX(d,i) - 3; | |
}, | |
// no gap between stacks | |
width: xScale.rangeBand() + (xScale.rangeBand() * bandPadding) + 2 | |
}); | |
// ** Exit ** interaction bars | |
interactionRects.exit().remove(); | |
}; | |
// Greyed out outline rects | |
// ---------------------------------- | |
var updateOutlineRects = function(){ | |
// Creates and updates the rect representing the combined stacks | |
var stackGroups = groups.stackGroups; | |
// Setup Invisible interaction rect for each group | |
// ------------------------------ | |
var outlineRects = stackGroups.selectAll('.outlineRect') | |
.data(function(d, i){ | |
// Wrap each array of group items in an array so only | |
// one item is added below for each rect | |
return [d]; | |
}); | |
// ** Enter ** interaction bars | |
outlineRects.enter().append('rect') | |
.attr({ | |
'class': function(d,i){ | |
return 'outlineRect outline' + i; | |
}, | |
x: helpers.stackX, | |
y: helpers.y0, | |
height: 0, | |
width: xScale.rangeBand() | |
}); | |
// ** Update ** | |
outlineRects.transition().duration(duration) | |
.attr({ | |
y: helpers.stackY, | |
height: helpers.stackHeight, | |
x: helpers.stackX, | |
width: xScale.rangeBand() | |
}); | |
// ** Exit ** outline bars | |
outlineRects.exit().transition().duration(duration) | |
.style({ opacity: 0 }) | |
.remove(); | |
}; | |
// Rects for net impact | |
// ---------------------------------- | |
var updateNetRects = function(){ | |
// Draws net impact rects for each stacked group | |
// | |
var stackGroups = groups.stackGroups; | |
// Net Bars | |
// ------------------------------ | |
var netRects = stackGroups.selectAll('.netRect') | |
.data(function(d, i){ | |
// Wrap each array of group items in an array so only | |
// one item is added below for each rect | |
return [d]; | |
}); | |
// ** Enter ** | |
// Draw bars for the net impact | |
netRects.enter().append('svg:rect') | |
.attr({ | |
'class': function(d,i){ | |
return 'netRect net' + i; | |
}, | |
width: xScale.rangeBand(), | |
x: helpers.stackX, | |
y: yScale(0), | |
height: 0 | |
}) | |
.style({ | |
fill: helpers.colorFill, | |
stroke: helpers.colorStroke, | |
'stroke-width': '2px' | |
}); | |
// ** Update ** | |
netRects.transition().duration(duration) | |
.attr({ | |
width: xScale.rangeBand(), | |
x: helpers.stackX, | |
y: helpers.netY, | |
height: helpers.netHeight | |
}); | |
// ** Exit ** | |
netRects.exit().transition().duration(duration) | |
.attr({ height: 0 }) | |
.style({ opacity: 0 }) | |
.remove(); | |
}; | |
// Rects for individual stack items | |
// ---------------------------------- | |
var updateStackRects = function(){ | |
// This function is called to: | |
// 1. initially create the stacked bars | |
// 2. update stacked bars on all subsequent calls | |
// Setup groups | |
var stackRectsGroup = groups.stackGroups | |
.selectAll('.stackRectsGroup') | |
.data(function(d){ | |
// return a single array of the stack items. | |
return [d]; | |
}); | |
// ** Enter - group ** | |
stackRectsGroup.enter() | |
.append('g') | |
.attr({ | |
'class': function(d,i){ | |
return 'stackRectsGroup stackRectsGroup' + i; | |
} | |
}).style({ opacity: 0 }); | |
// ** Update - group ** | |
stackRectsGroup.style({ opacity: 0 }); | |
// ** Exit - group ** | |
stackRectsGroup.exit().transition().duration(duration) | |
.style({ opacity: 0 }) | |
.remove(); | |
// Setup individual stack items | |
// ------------------------------ | |
stackRects = stackRectsGroup.selectAll('.stackRect') | |
.data(function(d){ return d; }); | |
// ** Enter ** | |
stackRects.enter().append('svg:rect') | |
.attr({ 'class': 'stackRect' }) | |
.style({ | |
fill: helpers.colorFill, | |
stroke: '#ffffff', | |
'stroke-width': '2px' | |
}) | |
// Stack Item Interaction | |
.on('mouseenter', function(d, i){ | |
// TODO: Show tooltip | |
console.log('>>> Current item: ', d, 'Index: ', i); | |
}); | |
// ** Update ** | |
// No duration, since these are hidden at first | |
stackRects.attr({ | |
// Position in middle of net rect | |
x: function(d){ | |
return xScale(d.group); | |
}, | |
width: xScale.rangeBand(), | |
y: function(d,i){ return yScale(d.y0); }, | |
height: function(d,i){ return yScale(0) - yScale(d.size); } | |
////NOTE: If we want stack rects to start in middle of net bar, | |
////use this: | |
//y: function(d,i,dataIndex){ | |
//return helpers.netY(data[dataIndex]) + | |
//(helpers.netHeight(data[dataIndex]) / 2); | |
//}, | |
//height: 0 | |
}); | |
//** Exit ** | |
// handles if an individual stacked item is removed | |
stackRects.exit().transition() | |
.duration(duration) | |
.attr({ height: 0 }) | |
.style({ opacity: 0 }) | |
.remove(); | |
}; | |
// Lines from net impact to axis | |
// ---------------------------------- | |
var updateNetLines = function(){ | |
// Draws a line from the net bar to the axis | |
// | |
var stackGroups = groups.stackGroups; | |
// Net Bars | |
// ------------------------------ | |
var netLines = stackGroups.selectAll('.netLine') | |
.data(function(d, i){ return [d]; }); | |
// ** Enter ** | |
// Draw line to the axis | |
netLines.enter().append('svg:line') | |
.attr({ | |
'class': function(d,i){ | |
return 'netLine netLine' + i; | |
}, | |
x1: 0, | |
x2: 0, | |
y1: helpers.netSumY, | |
y2: helpers.netSumY | |
}) | |
.style({ | |
}); | |
// ** Update ** | |
// No need to transition, lines only show on interaction | |
// | |
// * note * the x1 property will be transitioned to the axis | |
// on interaction (returned from calling helpers.yAxisLocation) | |
netLines.attr({ | |
x1: helpers.netMidX, | |
x2: helpers.netMidX, | |
y1: helpers.netSumY, | |
y2: helpers.netSumY | |
}); | |
// ** Exit ** | |
netLines.exit() | |
.attr({ height: 0 }) | |
.style({ opacity: 0 }) | |
.remove(); | |
}; | |
// Lines from net impact to axis | |
// ---------------------------------- | |
var updateNetDots = function(){ | |
// Draws a line from the net bar to the axis | |
// | |
var stackGroups = groups.stackGroups; | |
// Net Bars | |
// ------------------------------ | |
var netDots = stackGroups.selectAll('.netDot') | |
.data(function(d, i){ return [d]; }); | |
// ** Enter ** | |
// Draw line to the axis | |
netDots.enter().append('svg:circle') | |
.attr({ | |
'class': function(d,i){ | |
return 'netDot netDot' + i; | |
}, | |
cx: helpers.netMidX, | |
cy: helpers.netSumY, | |
r: 6 | |
}).style({ | |
opacity: 0 | |
}); | |
// ** Update ** | |
// No need to transition, lines only show on interaction | |
// | |
// * note * the x1 property will be transitioned to the axis | |
// on interaction (returned from calling helpers.yAxisLocation) | |
netDots.attr({ | |
cx: helpers.netMidX, | |
cy: helpers.netSumY | |
}); | |
// ** Exit ** | |
netDots.exit() | |
.attr({ height: 0 }) | |
.style({ opacity: 0 }) | |
.remove(); | |
}; | |
// ---------------------------------- | |
// Update Chart stack sub components | |
// ---------------------------------- | |
var updateStacks = function updateStacks(){ | |
// Main update chart function. Sets up and updates all components of the | |
// stacked chart | |
// | |
// Overview of the SVG structure | |
// 1. Group for each 'stack' | |
// 2. Invisible rect behind everything for interaction | |
// 3. Rect representing range of pos / neg items | |
// 4.1 Rect repesenting net impact (hidden on mouse over) | |
// 4.2 Rects for each policy (shown on mouse over) | |
// | |
// Group controls mouse interaction behavior | |
updateStackGroups(); | |
updateInteractionRects(); | |
updateOutlineRects(); | |
updateNetRects(); | |
updateStackRects(); | |
updateNetLines(); | |
updateNetDots(); | |
}; | |
// ----------------------------------------------------------------------- | |
// Main Chart Function | |
// ----------------------------------------------------------------------- | |
var chart = function chart(){ | |
// Update the svg properties | |
svg.attr({ | |
height: height, | |
width: width | |
}); | |
// When this is called, disable any mouse interaction when things are | |
// transitioning | |
var blocker = svg.append('svg:rect') | |
.attr({ | |
x: 0, | |
y: 0, | |
width: width, | |
height: height | |
}) | |
.style({ opacity: 0 }); | |
setTimeout(function(){ | |
// remove the blocking rect after transitions are finished | |
blocker.remove(); | |
}, duration); | |
// Format data | |
data = helpers.setupStack(data); | |
// Setup axes, chart, etc | |
updateScales(); | |
updateAxis(); | |
updateStacks(); | |
// we're not on the first draw anymore | |
isFirstDraw = false; | |
return chart; | |
}; | |
// ---------------------------------- | |
// Helper functions | |
// ---------------------------------- | |
var helpers = {}; | |
helpers.stackX= function(d,i){ | |
//Takes in either an array of stack objects or a single stack item and | |
//returns the x coordinate for it | |
if(d instanceof Array){ d = d[0]; } | |
var x = xScale(d.group); | |
return x; | |
}; | |
helpers.stackY = function(d,i){ | |
return yScale(d3.max(d, function(datum){ | |
return datum.y0; | |
})); | |
}; | |
helpers.stackHeight = function(d,i){ | |
var size = d3.sum(d, function(datum){ | |
return datum.size; | |
}); | |
return yScale(0) - yScale(size); | |
}; | |
// Net Positions | |
// ---------------------------------- | |
helpers.netMidX = function(d,i){ | |
// Mid point of x for passed in datum | |
// parameters: d {Object} array of stack items | |
return xScale(d[0].group) + (xScale.rangeBand()/2); | |
}; | |
helpers.netEndX = function(d,i){ | |
// Mid point of x for passed in datum | |
// parameters: d {Object} array of stack items | |
return xScale(d[0].group) + xScale.rangeBand(); | |
}; | |
helpers.netY = function(d,i){ | |
// Returns where the starting y value should be | |
// parameters: d {Object} array of stack items | |
var y = yScale(helpers.getGroupSum(d)); | |
// if the net impact is negative, y needs to start at the | |
// zero line | |
if(y > helpers.y0){ | |
y = yScale(0); | |
} | |
return y; | |
}; | |
helpers.netHeight = function(d,i){ | |
// Height is calculated by either y0 - y (total height) | |
// or y - y0 | |
// parameters: d {Object} array of stack items | |
var y = yScale(helpers.getGroupSum(d)); | |
var height = helpers.y0 - y; | |
if( y > helpers.y0){ | |
height = y - helpers.y0; | |
} | |
return height; | |
}; | |
helpers.netSumY = function(d){ | |
// returns the y value for sum of net impact of the passed in stack | |
// parameters: d {Object} array of stack items | |
// * note * this is called for the net line and net dot to position | |
// the y value | |
return yScale(helpers.getGroupSum(d)); | |
}; | |
// Axis location | |
helpers.yAxisLocation = function(){ | |
return xScale(data[0][0].group) - chartLeftMargin; | |
}; | |
// Colors | |
// ---------------------------------- | |
helpers.colorFill = function(d,i){ | |
var color = styles.colors.positive; | |
if(d instanceof Array){ | |
var sum = helpers.getGroupSum(d); | |
if(sum<0){ color = styles.colors.negative; } | |
} else { | |
if(d.value<0){ color = styles.colors.negative; } | |
} | |
return color; | |
}; | |
helpers.colorStroke = function(d,i){ | |
var color = helpers.colorFill(d,i); | |
color = d3.rgb(color).hsl().darker(); | |
return color; | |
}; | |
// Aggregate / Group functions | |
// ---------------------------------- | |
helpers.getGroupSum = function(d){ | |
// Sum the negative and positives for a passed in stack group | |
var neg = 0; | |
var pos = 0; | |
var len=d.length; | |
var curVal = 0; | |
for(var i=0; i<len; i++){ | |
curVal = d[i].value; | |
if(curVal < 0){ neg += curVal; } | |
else if(curVal >= 0){ pos += curVal; } | |
} | |
return pos + neg; | |
}; | |
helpers.setupStack = function setupStack(origData){ | |
// Formats the passed in data object to be in a format our | |
// chart can consume. | |
// *note*: This will modify the passed in object. If you don't want | |
// this behavior, you can clone the object (e.g., use underscore's | |
// clone method: origData = _.clone(origData) | |
// | |
// The setup data will be an array of arrays, each object in the | |
// subarray being an object representing an individual "stack". There | |
// are two added properties, `y0` and `size`, which specify the y | |
// position and `height` of the stack item. When created the bars, | |
// use these properties to position and size the bar | |
// | |
// setup some variables | |
var len = origData.length; | |
var i=0; j=0, d=null; | |
var basePositive=0, baseNegative=0; | |
for(i=0;i<len;i++){ // loop through each stacked group | |
// reset bases for each new group | |
basePositive = 0; | |
baseNegative = 0; | |
for(j=0; j<origData[i].length; j++){ // loop through each stack | |
stackRect = origData[i][j]; | |
stackRect.size = Math.abs(stackRect.value); | |
// If the value is negative, we want to place the bar under | |
// the 0 line | |
if (stackRect.value < 0) { | |
stackRect.y0 = baseNegative; | |
baseNegative -= stackRect.size; | |
} else { | |
basePositive += stackRect.size; | |
stackRect.y0 = basePositive; | |
} | |
} | |
} | |
//// To see the format of the data: | |
//console.log(JSON.stringify(origData, null, 4)); | |
return origData; | |
}; | |
// ---------------------------------- | |
// Chart Getter / setters | |
// ---------------------------------- | |
chart.width = function(value) { | |
if (!arguments.length){ return width; } | |
width = value; | |
return chart; | |
}; | |
chart.height = function(value) { | |
if (!arguments.length){ return height; } | |
height = value; | |
return chart; | |
}; | |
chart.margin = function(value) { | |
if (!arguments.length){ return margin; } | |
margin = value; | |
return chart; | |
}; | |
chart.data = function(value) { | |
if (!arguments.length){ return data; } | |
data = value; | |
return chart; | |
}; | |
chart.duration = function(value) { | |
if (!arguments.length){ return duration; } | |
duration= value; | |
return chart; | |
}; | |
return chart; | |
}; | |
// --------------------------------------------------------------------------- | |
// Create the chart | |
// --------------------------------------------------------------------------- | |
var chart = StackedChart({ | |
// pass in some data | |
data: getData(), | |
// transition duration | |
duration: 500 | |
}); | |
chart(); | |
//// further calls to chart() will update it | |
//setInterval(function(){ | |
//// we can also change any exposed property | |
////chart.margin(Math.random() * 20 ) | |
////.height(Math.random() * 800 ) | |
////.width(Math.random() * 800 ) | |
////.data( getData() ) | |
////(); | |
//// pass in new data then generate chart | |
//chart.data(getData())(); | |
//}, 4000); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.taxes, .services { | |
stroke: #ffffff; | |
stroke-width: 2px; | |
} | |
/* give outer paths a stroke so there isn't a white border */ | |
.outer path { | |
stroke: rgba(255,255,255,1); | |
stroke-width: 1px; | |
} | |
.innerActive { | |
stroke: #343434 !important; | |
stroke-width: 3px; | |
} | |
.bar.positive { | |
fill: steelblue; | |
} | |
.bar.negative { | |
fill: brown; | |
} | |
.axis text { | |
font: 10px sans-serif; | |
} | |
.axis.y path, | |
.axis.y line { | |
fill: none; | |
stroke: #909090; | |
shape-rendering: crispEdges; | |
} | |
.axis.y .tick line { | |
stroke: #eaeaea; | |
} | |
.line { | |
fill: none; | |
stroke: #b0b0b0; | |
stroke-width: 2px; | |
} | |
.lineDot { | |
fill: #ffffff; | |
stroke: #a0a0a0; | |
stroke-width: 4px; | |
} | |
.netLine { | |
stroke: #505050; | |
stroke-width: 1px; | |
} | |
.outlineRect { | |
fill: #f4f4f4; | |
stroke: #f0f0f0; | |
} | |
.netDot { | |
fill: #cdcdcd; | |
stroke: #ffffff; | |
stroke-width: 3px; | |
} | |
.zeroLine { | |
stroke: #343434; | |
stroke-width: 1px; | |
} | |
.annotationText { | |
font-family: arial; | |
font-size: 1em; | |
text-anchor: end; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment