Using a new updatable chart format. Update functions are made accessible to the caller, handing over chart controls with full functionality to the caller in a modular manner. Data binding is done with method chaining, like any other configuration variable, and can be changed after initialization. This allows for changes to be rendered in the context of chart history, leveraging D3's transitions and update logic.
-
-
Save gcalmettes/2e9bd0fbcb479a2f5878a89671b84753 to your computer and use it in GitHub Desktop.
A New Pattern for Updatable D3.js Charts
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
function barChart() { | |
// All options that should be accessible to caller | |
var width = 500; | |
var height = 300; | |
var barPadding = 1; | |
var fillColor = 'coral'; | |
var data = []; | |
var updateWidth; | |
var updateHeight; | |
var updateFillColor; | |
var updateData; | |
function chart(selection){ | |
selection.each(function () { | |
var barSpacing = height / data.length; | |
var barHeight = barSpacing - barPadding; | |
var maxValue = d3.max(data); | |
var widthScale = width / maxValue; | |
var dom = d3.select(this); | |
var svg = dom.append('svg') | |
.attr('class', 'bar-chart') | |
.attr('height', height) | |
.attr('width', width) | |
.style('fill', fillColor); | |
var bars = svg.selectAll('rect.display-bar') | |
.data(data) | |
.enter() | |
.append('rect') | |
.attr('class', 'display-bar') | |
.attr('y', function (d, i) { return i * barSpacing; }) | |
.attr('height', barHeight) | |
.attr('x', 0) | |
.attr('width', function (d) { return d * widthScale; }); | |
// update functions | |
updateWidth = function() { | |
widthScale = width / maxValue; | |
bars.transition().duration(1000).attr('width', function(d) { return d * widthScale; }); | |
svg.transition().duration(1000).attr('width', width); | |
}; | |
updateHeight = function() { | |
barSpacing = height / data.length; | |
barHeight = barSpacing - barPadding; | |
bars.transition().duration(1000).attr('y', function(d, i) { return i * barSpacing; }) | |
.attr('height', barHeight); | |
svg.transition().duration(1000).attr('height', height); | |
}; | |
updateFillColor = function() { | |
svg.transition().duration(1000).style('fill', fillColor); | |
}; | |
updateData = function() { | |
barSpacing = height / data.length; | |
barHeight = barSpacing - barPadding; | |
maxValue = d3.max(data); | |
widthScale = width / maxValue; | |
var update = svg.selectAll('rect.display-bar') | |
.data(data); | |
update | |
.transition() | |
.duration(1000) | |
.attr('y', function(d, i) { return i * barSpacing; }) | |
.attr('height', barHeight) | |
.attr('x', 0) | |
.attr('width', function(d) { return d * widthScale; }); | |
update.enter() | |
.append('rect') | |
.attr('class', 'display-bar') | |
.attr('y', function(d, i) { return i * barSpacing; }) | |
.attr('height', barHeight) | |
.attr('x', 0) | |
.attr('width', 0) | |
.style('opacity', 0) | |
.transition() | |
.duration(1000) | |
.delay(function(d, i) { return (data.length - i) * 40; }) | |
.attr('width', function(d) { return d * widthScale; }) | |
.style('opacity', 1); | |
update.exit() | |
.transition() | |
.duration(650) | |
.delay(function(d, i) { return (data.length - i) * 20; }) | |
.style('opacity', 0) | |
.attr('height', 0) | |
.attr('x', 0) | |
.attr('width', 0) | |
.remove(); | |
} | |
}); | |
} | |
chart.width = function(value) { | |
if (!arguments.length) return width; | |
width = value; | |
if (typeof updateWidth === 'function') updateWidth(); | |
return chart; | |
}; | |
chart.height = function(value) { | |
if (!arguments.length) return height; | |
height = value; | |
if (typeof updateHeight === 'function') updateHeight(); | |
return chart; | |
}; | |
chart.fillColor = function(value) { | |
if (!arguments.length) return fillColor; | |
fillColor = value; | |
if (typeof updateFillColor === 'function') updateFillColor(); | |
return chart; | |
}; | |
chart.data = function(value) { | |
if (!arguments.length) return data; | |
data = value; | |
if (typeof updateData === 'function') updateData(); | |
return chart; | |
}; | |
return chart; | |
} |
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
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>Updatable Charts (4 of 4)</title> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> | |
<script src="chart.js"></script> | |
<style> | |
div { | |
padding: 20px 0 0 10px; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="updatableChart"></div> | |
<script> | |
var dataSet = []; | |
var highTemperatures = dataSet[0] = [77, 71, 82, 87, 84, 78, 80, 84, 86, 72, 71, 68, 75, 73, 80, 85, 86, 80]; | |
var lowTemperatures = dataSet[1] = highTemperatures.map(function(d) { return d - Math.random() * 30}); | |
var milesRun = dataSet[2] = [2, 5, 4, 3, 1, 2, 1]; | |
var fillColors = ['coral', 'steelblue', 'teal']; | |
var updatableChart = barChart().width(800).data(highTemperatures); | |
d3.select('#updatableChart') | |
.call(updatableChart); | |
window.setTimeout(function() { | |
updatableChart.height(450); | |
}, 1000); | |
var i = 1; | |
window.setInterval(function() { | |
updatableChart.data(dataSet[i]); | |
updatableChart.fillColor(fillColors[i]); | |
i = (i+1) % 3 ; | |
}, 2500); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment