|
<!DOCTYPE html> |
|
<html> |
|
<head> |
|
<style> |
|
body { |
|
font-family: "Helvetica Neue", sans-serif; |
|
} |
|
|
|
.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: 40, bottom: 40, left: 40, right: 40}, |
|
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), |
|
x_axis = d3.axisBottom() |
|
.scale(x_scale), |
|
y_axis = d3.axisLeft() |
|
.scale(y_scale); |
|
|
|
svg.append("g") |
|
.attr("class", "x axis") |
|
.attr("transform", "translate(0," + height + ")") |
|
.call(x_axis); |
|
|
|
svg.append("g") |
|
.attr("class", "y axis") |
|
.call(y_axis); |
|
|
|
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 |
|
dot_a.exit() |
|
.transition(t) |
|
.style("r", 1e-6) |
|
.remove(); |
|
|
|
dot_b.exit() |
|
.transition(t) |
|
.style("r", 1e-6) |
|
.remove(); |
|
|
|
connect_line.exit() |
|
.transition(t) |
|
.style("opacity", 1e-6) |
|
.remove(); |
|
|
|
// UPDATE |
|
dot_a |
|
.transition(t) |
|
.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) |
|
.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) |
|
.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) |
|
.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) |
|
.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("y1", function(d){ return y_scale(d.y_a) + (size_scale(d.size_a) * multiplier(d.y_a, d.y_b)); }) |
|
.attr("x2", function(d){ return x_scale(d.x); }) |
|
.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 = bin_size; i <= 95; 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); |
|
} |
|
|
|
</script> |
|
|
|
</body> |
|
</html> |