block by nbremer 36cc042b51f605183d2a2cce686653e1

Motion blur - Top running speeds

Full Screen

This example was used in my blog on Creating real-life based motion effects in d3.js visuals, which is part of the SVGs beyond mere shapes series

This chart visualizes the top running speed of several animals and the fastest human. When the circles move outward they get blurred to mimic the idea of motion blur that we see in real life. The faster a circle (and thus animal) moves, the more blurred it gets

Click anywhere to move the circles back in and again to move them back

Other examples about motion blur are

Data from SpeedOfAnimals.com

Built with blockbuilder.org

index.html

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		
		<!-- D3.js -->
		<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>

		<!-- Google fonts -->
		<link href='https://fonts.googleapis.com/css?family=Open+Sans:300,400' rel='stylesheet' type='text/css'>

		<style>
			#animalSpeeds {
				text-align: center;
				font-family: 'Open Sans', sans-serif;
				font-weight: 400;
			}

			#animalSpeeds .title {
			    font-size: 36px;
			    fill: #4F4F4F;
			    font-weight: 300;
			}

			#animalSpeeds .axisTitle {
				text-anchor: middle;
				fill: #6B6B6B;
				font-size: 16px;
				font-weight: 300;
			}	

			#animalSpeeds .animalLabel {
				text-anchor: end;
				fill: #8C8C8C;
				font-size: 16px;
				font-weight: 300;		
			}

			#animalSpeeds .animalLabel.human {
				fill: #F92672;	
			}

			#animalSpeeds .axis path,
			#animalSpeeds .axis line {
				fill: none;
			  	stroke: #C4C4C4;
			  	stroke-width: 1px;
			  	shape-rendering: crispEdges;
			}

			#animalSpeeds .axis text {
				fill: #8C8C8C;
				font-size: 14px;
				font-weight: 300;
			}

			#animalSpeeds .credit {
		    	font-size: 14px;
		    	fill: #AAAAAA;
		    	font-weight: 300;
		    	text-anchor: middle;
		  	}
		</style>

	</head>	
	<body>

		<div id="animalSpeeds"></div>

		<script language="javascript" type="text/javascript">

			///////////////////////////////////////////////////////////////////////////
			//////////////////// Set up and initiate svg containers ///////////////////
			///////////////////////////////////////////////////////////////////////////	

			var margin = {
				top: 200,
				right: 30,
				bottom: 50,
				left: 150
			};

			var width = Math.min(1000, document.getElementById('animalSpeeds').offsetWidth) - margin.left - margin.right - 10,
				height = width*2/3;//Math.min(window.innerHeight - margin.top - margin.bottom - 20, width*2/3);

			//SVG container
			var svg = d3.select("#animalSpeeds")
				.append("svg")
				.attr("width", width + margin.left + margin.right)
				.attr("height", height + margin.top + margin.bottom)
				.on("click", flyOut)
				.append("g")
				.attr("transform", "translate(" + margin.left + "," + margin.top + ")");

			///////////////////////////////////////////////////////////////////////////
			//////////////////////////////// Create Data //////////////////////////////
			///////////////////////////////////////////////////////////////////////////	

			var animals = [
				{id: 1, animal: 'Cheetah', speed: 120, image: 'cheetah'},
				{id: 2, animal: 'Horse', speed: 88, image: 'horse'},
				{id: 3, animal: 'Lion', speed: 80, image: 'lion'},
				{id: 4, animal: 'Ostrich', speed: 70, image: 'ostrich'},
				{id: 5, animal: 'Greyhound', speed: 63.5, image: 'greyhound'},
				{id: 6, animal: 'Grizzly bear', speed: 56, image: 'grizzly_bear'},
				{id: 7, animal: 'Rabbit', speed: 48, image: 'rabbit'},
				{id: 8, animal: 'Cat', speed: 48, image: 'cat'},
				{id: 9, animal: 'Usain Bolt', speed: 45, image: 'human'},
				{id: 10, animal: 'Squirrel', speed: 20, image: 'squirrel'},
				{id: 11, animal: 'House mouse', speed: 13, image: 'house_mouse'},
				{id: 12, animal: 'Giant tortoise', speed: 0.3, image: 'tortoise'}
			];

			///////////////////////////////////////////////////////////////////////////
			//////////////////////////// Create fuzzy filter //////////////////////////
			///////////////////////////////////////////////////////////////////////////	

			//SVG filter for the fuzzy effect
			//Code based on //tympanus.net/codrops/2015/04/08/motion-blur-effect-svg/
			var defs = svg.append("defs");

			defs.selectAll("filter")
				.data(animals)
				.enter().append("filter")
				.attr("id",function(d,i) { return "motionAnimalFilter-" + d.id; })
				.attr("width", "500%")	//increase the width of the filter region to remove blur "boundary"
				.attr("x", "-200%") //make sure the center of the "width" lies in the middle
				.attr("color-interpolation-filters","sRGB") //to fix safari: //stackoverflow.com/questions/24295043/svg-gaussian-blur-in-safari-unexpectedly-lightens-image
				.append("feGaussianBlur")
				.attr("class", function(d,i) { return "blurValues"; })
				.attr("in","SourceGraphic")
				.attr("stdDeviation","0,0");

			///////////////////////////////////////////////////////////////////////////
			////////////////////////// Create title and credit ////////////////////////
			///////////////////////////////////////////////////////////////////////////	

			//Title on top
		    svg.append("text")
		    	.attr("class", "title")
		        .attr("x", width/2)
		        .attr("y", - 120)
		        .style("text-anchor", "middle")
		        .text("Top running speeds");
		    //Subtitle
		    svg.append("text")
		    	.attr("class", "credit")
		        .attr("x", width/2)
		        .attr("y", - 95)
		        .style("text-anchor", "middle")
		        .text("Click anywhere to move the circle back in/out");

		  	///////////////////////////////////////////////////////////////////////////
			////////////////////////// Create scales and axes /////////////////////////
			///////////////////////////////////////////////////////////////////////////	
		      
			var axisGroup = svg.append("g").attr("class", "axisWrapper");

			var maxSpeed = d3.max(animals, function(d) { return d.speed; });
			var kmScale = d3.scale.linear()
				.range([0, width])
				.domain([0, maxSpeed]);

			var milesScale = d3.scale.linear()
				.range([0, width])
				.domain([0, 0.621371192*d3.max(animals, function(d) { return d.speed; })] );

		    //Bottom x axis
		    var xAxisBottom = d3.svg.axis()
		        .scale(kmScale)
		        .orient("bottom")
		        .tickFormat(d3.format(".0f"))
		        //.outerTickSize(0)
		        .ticks(5);
			//Add the X bottom Axis
		    axisGroup.append("g")
		        .attr("class", "x axis")
		        .attr("transform", "translate(0," + (height + -15) + ")")
		        .call(xAxisBottom);
		    //Append x-axis bottom title
		    axisGroup.append("text")
		    	.attr("class", "axisTitle")
		    	.attr("x", width/2)
		    	.attr("y", height + 30)
		    	.text("Speed [km/h]");

		    //Top x axis
		    var xAxisTop = d3.svg.axis()
		        .scale(milesScale)
		        .orient("top")
		        .ticks(5);
		    //Add the X top Axis
		    axisGroup.append("g")
		        .attr("class", "x axis")
		        .attr("transform", "translate(0," + -30 + ")")
		        .call(xAxisTop); 
		    //Append x-axis top title
		    axisGroup.append("text")
		    	.attr("class", "axisTitle")
		    	.attr("x", width/2)
		    	.attr("y", -63)
		    	.text("Speed [mph]");

			///////////////////////////////////////////////////////////////////////////
			/////////////////////////// Create circles ////////////////////////////////
			///////////////////////////////////////////////////////////////////////////	
			
			var radius = Math.min(8, width/100);
			
			//Set up the circle wrapper
			animalGroups = svg.selectAll(".animalGroups")
				.data(animals)
				.enter().append("g")
				.attr("class", "animalGroups")
				.attr("transform", function(d,i) { return "translate(0," + i*(height/animals.length) + ")"; });

			//Label at the start
			animalGroups.append("text")
				.attr("class", function(d) { return "animalLabel " + d.image; })
				.attr("x", -40)
				.attr("dy", "0.3em")
				.text(function(d) { return d.animal; });

			//Circles
			animalGroups.append("circle")
				.attr("class", "animalCircle")
				.attr("cx", 0)
				.attr("cy", 0)
				.attr("r", radius)
				.style("fill", "#F92672")
				.style("filter", function(d,i) { return "url(#motionAnimalFilter-" + d.id + ")"; });


			///////////////////////////////////////////////////////////////////////////
			//////////////////// Move the circles outward and in //////////////////////
			///////////////////////////////////////////////////////////////////////////	

			//Move the circles to their speed and blur them according to how fast they move outward
			function flyOut() {

				var dur = 1200;

				//Interpolate the fuzzyness
				d3.selectAll(".blurValues")
					.transition("fuzzyIn").duration(dur*0.4) //Outward movement
					//.delay(function(d,i) { return (i+1)*100 + 200; })
					.delay(1000)
					.attrTween("stdDeviation", function(d) { 
						d.maxValue = d.speed * 12 / maxSpeed;
						return d3.interpolateString("0 0", d.maxValue+" 0"); 
					})
				  .transition("fuzzyOut").duration(dur*0.3)
				  .attrTween("stdDeviation", function(d) { return d3.interpolateString(d.maxValue+" 0", "0 0"); });
        
				//Move the circles outward
				d3.selectAll(".animalCircle")
					.transition("changeRadius").duration(500)
					.attr("cx", 0)
					.transition("moveOut").duration(dur)
					.delay(800)
					.attr("cx", function(d,i) { return kmScale(d.speed); });

			}//flyOut

			//Move the circles outward first
			flyOut();

		</script>

	</body>
</html>