block by HarryStevens 24a5d350bad650e1882707e5e523c360

Staggered Barbells

Full Screen

Add a staggered transition to a barbell chart.

index.html

<!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 src="https://unpkg.com/jeezy/lib/jeezy.min.js"></script>
		<script src="https://unpkg.com/d3-marcon/build/d3-marcon.min.js"></script>
		<script>

		// config
		var setup = d3.marcon().top(30).bottom(30).left(30).right(30).width(window.innerWidth).height(window.innerHeight);
		setup.render();
		var width = setup.innerWidth(),
			height = setup.innerHeight(),
			svg = setup.svg(),
			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: jz.num.randBetween(1, 30),
					size_b: jz.num.randBetween(1, 30), 
					y_a: jz.num.randBetween(1, 1000),
					y_b: jz.num.randBetween(1, 1000)
				});

			}

			return arr;

		}

		// a staggered transition
		function stagger(d, i, time) {
			return i * time;
		}

		</script>

	</body>
</html>