Skip to content

Instantly share code, notes, and snippets.

@RandomEtc
Last active February 12, 2017 15:39
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save RandomEtc/5165219 to your computer and use it in GitHub Desktop.
Save RandomEtc/5165219 to your computer and use it in GitHub Desktop.
Advanced object constancy.

Fine-grained control of randomly entering and exiting things in D3.js, for impeccable object constancy. Adapted from an example co-authored with Mike Bostock.

<!DOCTYPE html>
<html>
<head>
<script src="http://d3js.org/d3.v3.min.js"></script>
<style>
html, body {
width: 100%;
height: 100%;
overflow: hidden;
margin: 0;
padding: 0;
}
circle {
fill: black;
stroke: none;
}
circle.saved {
fill: green;
}
</style>
</head>
<body>
<script>
var w = 900,
h = 500,
svg = d3.select('body').append('svg')
.attr('width',w)
.attr('height',h);
var data = [],
removed = [];
function render() {
var circles = svg.selectAll('circle')
.data(data, function(d){
return d.id;
});
// the enter() selection needs representation:
circles.enter().append('circle')
.classed('entering',true)
.attr('cx',function(d){ return d.center.x; })
.attr('cy',function(d){ return d.center.y; })
.attr('r',100)
.style('opacity',1e-6);
// the exit() selection needs removing
circles.exit().filter(':not(.exiting)') // but only if we didn't already
.classed('exiting',true)
.transition().duration(1000)
.attr('r',100)
.style('opacity',1e-6)
.remove();
// just for debugging this demo, show which things were saved
// circles.classed('saved',function(d){ return d.saved; });
// two things here:
// - if something is currently exiting (but in this selection),
// we should cancel its exiting transition and restore it
// - if something is currently entering (it was *just* added),
// we need to trigger a brand new transition
circles.filter('.exiting, .entering')
.classed('exiting',false)
.classed('entering',false)
.transition().duration(1000)
.attr('r',function(d){ return d.radius; })
.style('opacity',1.0);
console.log(data.length)
}
setInterval(function(){
// remove a random element ever other tick (or so)
if (data.length > 20 && Math.random() > 0.5) {
var index = Math.floor(Math.random() * data.length);
var item = data[index];
data.splice(index,1);
// save 1 in 4 removed thing for later restoration
if (Math.random() < 0.25) {
item.saved = true;
removed.push(item);
}
} else {
// if we have something to save, save it:
if (removed.length) {
data.push(removed.pop());
} else {
// otherwise create a new one
data.push({
id: new Date().getTime(),
center: {
x: Math.random() * w,
y: Math.random() * h
},
radius: 2 + Math.random() * 10
});
}
}
render();
}, 30);
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment