Skip to content

Instantly share code, notes, and snippets.

@jfsiii
Created June 6, 2016 20:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jfsiii/0d4f863fec8b56d94b534db97816cd67 to your computer and use it in GitHub Desktop.
Save jfsiii/0d4f863fec8b56d94b534db97816cd67 to your computer and use it in GitHub Desktop.
A New Pattern for Updatable D3.js Charts

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.

forked from rcmoore38's block: A New Pattern for Updatable D3.js Charts

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;
}
<!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>
@umarashfaq
Copy link

Don't you think your update functions (e.g. updateWidth) will work only if there is one item in your selection? And if there are more than one items, only the last item will get updated?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment