Skip to content

Instantly share code, notes, and snippets.

@richardbutler
Last active May 24, 2016 19:53
Show Gist options
  • Save richardbutler/a2708c4f062af5c400fa to your computer and use it in GitHub Desktop.
Save richardbutler/a2708c4f062af5c400fa to your computer and use it in GitHub Desktop.
D3's General Update Pattern and Flux

Facebook's Flux architecture was originally conceived for web apps that use React as its view layer. However, it is also the perfect foil for interactive applications built with D3, with its emphasis on functional reactive programming and immutability.

This quick prototype - a remake of Mike Bostock's General Update Pattern III - illustrates how this can be achieved using Reflux.

/*global d3,Reflux,_ */
(function (d3, Reflux, _) {
'use strict';
var alphabet = 'abcdefghijklmnopqrstuvwxyz'.split('');
var Shuffle = Reflux.createAction();
var LetterStore = Reflux.createStore({
data: alphabet,
listenables: {
'shuffle': Shuffle
},
getInitialState: function () {
return this.data;
},
shuffle: function () {
this.data = _
.shuffle(alphabet)
.slice(0, Math.floor(Math.random() * alphabet.length))
.sort();
this.trigger(this.data);
}
});
var View = (function () {
var width = 960;
var height = 500;
var svg = d3.select('body')
.append('svg')
.attr({
width: width,
height: height
})
.append('g')
.attr('transform', 'translate(32,' + (height / 2) + ')');
var transitionDuration = 750;
var yOffset = 60;
function render (data) {
// DATA JOIN
// Join new data with old elements, if any.
var text = svg.selectAll('text')
.data(data, _.identity);
// UPDATE
// Update old elements as needed.
text.attr('class', 'update')
.transition()
.duration(transitionDuration)
.attr('x', x);
// ENTER
// Create new elements as needed.
text.enter()
.append('text')
.attr({
'class': 'enter',
'dy': '.35em',
'y': -yOffset,
'x': x
})
.style('fill-opacity', 1e-6)
.text(_.identity)
.transition()
.duration(transitionDuration)
.attr('y', 0)
.style('fill-opacity', 1);
// EXIT
// Remove old elements as needed.
text.exit()
.attr('class', 'exit')
.transition()
.duration(transitionDuration)
.attr('y', yOffset)
.style('fill-opacity', 1e-6)
.remove();
return this;
}
function x (d, i) {
return i * 32;
}
return _.extend({
render: render
}, Reflux.ListenerMethods);
})();
// Wire the View upto the LetterStore
View.listenTo(LetterStore, 'render', 'render');
setInterval(Shuffle, 1500);
})(d3, Reflux, _);
<!DOCTYPE html>
<html>
<head>
<title>General Update Pattern: Fluxed</title>
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.js"></script>
<script src="//cdn.jsdelivr.net/refluxjs/0.4.1/reflux.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<style>
text {
font: bold 48px monospace;
}
.enter {
fill: green;
}
.update {
fill: #333;
}
.exit {
fill: brown;
}
</style>
</head>
<body>
<script src="app.js"></script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment