Add a staggered transition to a barbell chart.
forked from HarryStevens's block: Staggered Barbells
license: gpl-3.0 |
Add a staggered transition to a barbell chart.
forked from HarryStevens's block: Staggered Barbells
<!DOCTYPE html> | |
<html> | |
<head> | |
<style> | |
body { | |
font-family: "Helvetica Neue", sans-serif; | |
margin: 0; | |
} | |
.dot { | |
stroke-width: 1px; | |
fill-opacity: .6; | |
} | |
.dot.a { | |
stroke: steelblue; | |
fill: steelblue; | |
} | |
.dot.b { | |
stroke: tomato; | |
fill: tomato; | |
} | |
.connect-line { | |
stroke: #ccc; | |
stroke-width: 1px; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="viz"></div> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<script> | |
// config | |
var margin = {top: 30, bottom: 30, left: 30, right: 30}, | |
width = window.innerWidth - margin.left - margin.right, | |
height = window.innerHeight - margin.top - margin.bottom, | |
svg = d3.select("#viz").append("svg") | |
.attr("width", width + margin.left + margin.right) | |
.attr("height", height + margin.top + margin.bottom) | |
.append("g") | |
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"), | |
x_scale = d3.scaleLinear() | |
.range([0, width]) | |
.domain([0, 100]), | |
y_scale = d3.scaleLinear() | |
.range([height, 0]) | |
.domain([0, 1000]), | |
size_scale = d3.scaleLinear() | |
.range([1, 30]) | |
.domain([1, 30]), | |
t = d3.transition() | |
.duration(750), | |
stagger_time = 50; | |
draw_chart(generate_binned_data()); | |
d3.interval(function(){ | |
draw_chart(generate_binned_data()); | |
}, 2000); | |
function draw_chart(data){ | |
// JOIN | |
var dot_a = svg.selectAll(".dot.a") | |
.data(data, function(d){ return d.x; }); | |
var dot_b = svg.selectAll(".dot.b") | |
.data(data, function(d){ return d.x; }); | |
var connect_line = svg.selectAll(".connect-line") | |
.data(data, function(d){ return d.x }); | |
// EXIT | |
// Doesn't get used here but nice to have for copypasta | |
dot_a.exit() | |
.transition(t).delay(function(d, i){ return stagger(d, i, stagger_time); }) | |
.style("r", 1e-6) | |
.remove(); | |
dot_b.exit() | |
.transition(t).delay(function(d, i){ return stagger(d, i, stagger_time); }) | |
.style("r", 1e-6) | |
.remove(); | |
connect_line.exit() | |
.transition(t).delay(function(d, i){ return stagger(d, i, stagger_time); }) | |
.style("opacity", 1e-6) | |
.remove(); | |
// UPDATE | |
dot_a | |
.transition(t).delay(function(d, i){ return stagger(d, i, stagger_time); }) | |
.attr("cx", function(d){ return x_scale(d.x); }) | |
.attr("cy", function(d){ return y_scale(d.y_a); }) | |
.attr("r", function(d){ return size_scale(d.size_a); }); | |
dot_b | |
.transition(t).delay(function(d, i){ return stagger(d, i, stagger_time); }) | |
.attr("cx", function(d){ return x_scale(d.x); }) | |
.attr("cy", function(d){ return y_scale(d.y_b); }) | |
.attr("r", function(d){ return size_scale(d.size_b); }); | |
connect_line | |
.transition(t).delay(function(d, i){ return stagger(d, i, stagger_time); }) | |
.attr("y1", function(d){ return y_scale(d.y_a) + (size_scale(d.size_a) * multiplier(d.y_a, d.y_b)); }) | |
.attr("y2", function(d){ return y_scale(d.y_b) - (size_scale(d.size_b) * multiplier(d.y_a, d.y_b)); }) | |
// ENTER | |
dot_a.enter().append("circle") | |
.attr("class", "dot a") | |
.attr("cx", function(d){ return x_scale(d.x); }) | |
.attr("cy", function(d){ return y_scale(d.y_a); }) | |
.attr("r", 1e-6) | |
.transition(t).delay(function(d, i){ return stagger(d, i, stagger_time); }) | |
.attr("r", function(d){ return size_scale(d.size_a); }); | |
dot_b.enter().append("circle") | |
.attr("class", "dot b") | |
.attr("cx", function(d){ return x_scale(d.x); }) | |
.attr("cy", function(d){ return y_scale(d.y_b); }) | |
.attr("r", 1e-6) | |
.transition(t).delay(function(d, i){ return stagger(d, i, stagger_time); }) | |
.attr("r", function(d){ return size_scale(d.size_b); }); | |
connect_line.enter().append("line") | |
.attr("class", "connect-line") | |
.attr("x1", function(d){ return x_scale(d.x); }) | |
.attr("x2", function(d){ return x_scale(d.x); }) | |
.attr("y1", height / 2) | |
.attr("y2", height / 2) | |
.transition(t).delay(function(d, i){ return stagger(d, i, stagger_time); }) | |
.attr("y1", function(d){ return y_scale(d.y_a) + (size_scale(d.size_a) * multiplier(d.y_a, d.y_b)); }) | |
.attr("y2", function(d){ return y_scale(d.y_b) - (size_scale(d.size_b) * multiplier(d.y_a, d.y_b)); }) | |
} | |
// a or b on top | |
function multiplier(a, b){ | |
return a > b ? 1 : -1; | |
} | |
// make some data | |
function generate_binned_data(){ | |
var bin_size = 5, | |
arr = []; | |
for (var i = 0; i <= 100; i += bin_size){ | |
arr.push({ | |
x: i, | |
size_a: random(1, 30), | |
size_b: random(1, 30), | |
y_a: random(1, 1000), | |
y_b: random(1, 1000) | |
}); | |
} | |
return arr; | |
} | |
// random number function | |
function random(min, max){ | |
return Math.floor(Math.random() * (max - min + 1) + min); | |
} | |
// a staggered transition | |
function stagger(d, i, time) { | |
return i * time; | |
} | |
</script> | |
</body> | |
</html> |