Skip to content

Instantly share code, notes, and snippets.

@robert-moore
Last active January 17, 2021 08:38
Show Gist options
  • Save robert-moore/9f2908455355c0589619 to your computer and use it in GitHub Desktop.
Save robert-moore/9f2908455355c0589619 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.

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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment