block by nbremer 077ef487fefcff72a02605422298e2b3

Motion blur - Diagonal movement & Multiple filters

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 is an example of using a SVG gaussian blur filter in one direction to mimic motion blur that occurs in real life. Although the blur can only be applied in either the x or y direction, we can use a transform of a group element to make it appear as if the element has a blur along any angle. Also this example shows how to create, apply and manipulate a motion blur filter separately for each circle

Other examples about motion blur are

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>
			body {
				text-align: center;
				font-family: 'Open Sans', sans-serif;
				font-weight: 400;
			}

			.title {
			    font-size: 24px;
			    fill: #4F4F4F;
			    font-weight: 300;
			    text-anchor: middle;
			}

			#explanation {
				font-size: 12px;
				color: #737373;
			}
		</style>

	</head>	
	<body>

		<div id="movingCircles"></div>
		<div id="explanation">Click anywhere to turn on/off the motion blur filter</div>

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

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

			var margin = {
				top: 50,
				right: 50,
				bottom: 20,
				left: 50
			};
			var width = document.getElementById('movingCircles').offsetWidth - margin.left - margin.right - 10,
				height = 400;
						
			//SVG container
			var svg = d3.select('#movingCircles')
				.append("svg")
				.attr("width", width + margin.left + margin.right)
				.attr("height", height + margin.top + margin.bottom)
				.on("click", switchBlur)
				.append("g")
				.attr("transform", "translate(" + (margin.left + width/2) + "," + (margin.top + height/2) + ")");

			var color = "#F92672";

			//Title on top
		    svg.append("text")
		    	.attr("class", "title")
		        .attr("x", 0)
		        .attr("y", -height/2 - 20)
		        .style("text-anchor", "middle")
		        .text("With motion blur filter");

			///////////////////////////////////////////////////////////////////////////
			//////////////////// Switch between blur and no blur //////////////////////
			///////////////////////////////////////////////////////////////////////////	

			//Switch between blur filter and no filter on click
			var blurOn = true;
			function switchBlur() {

				//Change title
				d3.selectAll(".title")
	        		.text(blurOn ? "Without motion blur filter" : "With motion blur filter");

				d3.selectAll(".flyCircle")
					.style("filter", function(d,i) { return blurOn ? "none" : "url(#motionBlurFilter-"+i+")"; });

				blurOn = blurOn ? false : true;
			}//switchBlur

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

			//Create the circles that will move out and in the center circle
			var steps = 15;
			var flyCircleData = [];
			for (var i = 0; i < steps; i++) {
				flyCircleData.push({ 
					id: i,
					fixedAngle: (i/steps)*(2*Math.PI)+0.01
				})
			}//for i

			///////////////////////////////////////////////////////////////////////////
			//////////////////////////// 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");

			//Create a filter per circle so we can adjust the fuzzyness per circle that is flying out
			defs.selectAll(".flyCircleFilters")
				.data(flyCircleData)
				.enter().append("filter")
				.attr("class", "flyCircleFilters")
				.attr("id",function(d,i) { return "motionBlurFilter-"+i; })
				.attr("width", "300%")	//increase the width of the filter region to remove blur "boundary"
				.attr("x", "-100%") //make sure the center of the "width" lies in the middle
				.attr("height", "200%")
				.attr("y", "-50%")
				.attr("color-interpolation-filters","sRGB") //to fix safari: //stackoverflow.com/questions/24295043/svg-gaussian-blur-in-safari-unexpectedly-lightens-image
				.append("feGaussianBlur")
				.attr("class", "blurValues")
				.attr("in","SourceGraphic")
				.attr("stdDeviation","0,0");

			///////////////////////////////////////////////////////////////////////////
			//////////////////////// Create fly out circles ///////////////////////////
			///////////////////////////////////////////////////////////////////////////	

			var circleWrapper = svg.append("g")
				.attr("class", "circleWrapper");

			//Since we can only do a blur in the x and y direction the group should be rotated
			//so the coordinated system turns along with the direction the circle
			//will be flying out from
			var flyCircles = circleWrapper.selectAll(".flyCircle")
				.data(flyCircleData)
				.enter().append("circle")
				.attr("class", "flyCircle")
				.attr("transform", function(d,i) { return "rotate(" + (d.fixedAngle*180/Math.PI - 90) + ")"; })
				.attr("cx", 0)
				.attr("cy", 0)
				.attr("r", 12)
				.style("fill", color)
				.style("filter", function(d,i) { return "url(#motionBlurFilter-"+i+")"; });

			//Append circle at center
			circleWrapper.append("circle")
					.attr("class", "centerCircle")
					.attr("cx", 0)
					.attr("cy", 0)
					.attr("r", 25)
					.style("fill", color);

			repeat();

			///////////////////////////////////////////////////////////////////////////
			/////////////////////// Move in and out function //////////////////////////
			///////////////////////////////////////////////////////////////////////////	

			//Continuously moves the circles outward and inward	
			function repeat() {

				var dur = 1000,
					del = 500;	
				
				//Interpolate the fuzzyness
				d3.selectAll(".blurValues")
					.transition().duration(dur*0.1)
					.delay(function(d,i) { return i*del; })
					.attrTween("stdDeviation", function() { return d3.interpolateString("0 0", "9 0"); })
				  .transition().duration(dur*0.2)
				  .attrTween("stdDeviation", function() { return d3.interpolateString("9 0", "0 0"); })
					.transition().duration(dur*0.4) //Another one for the circles moving back in
					.delay(function(d,i) { return steps*del + i*del; })
					.attrTween("stdDeviation", function() { return d3.interpolateString("0 0", "9 0"); })
				  .transition().duration(dur*0.3)
				  .attrTween("stdDeviation", function() { return d3.interpolateString("9 0", "0 0"); });

				//Move circles in an out
				d3.selectAll(".flyCircle")
					.transition("flyOut").duration(dur)
					.delay(function(d,i) { return d.id*del; })
					.ease("elastic")
					.attr("cx", height/2*0.8 )
					.transition("flyIn").duration(dur/3)
					.delay(function(d,i) { return steps*del + d.id*del; })
					.ease("exp")
					.attr("cx", 0)
					.call(endall, repeat);

			}//repeat

			//Function to only run once after the last transition ends
			function endall(transition, callback) { 
				var n = 0; 
				transition 
					.each(function() { ++n; }) 
					.each("end", function() { if (!--n) callback.apply(this, arguments); }); 
			}//endall

		</script>

	</body>
</html>