Skip to content

Instantly share code, notes, and snippets.

@nbremer
Last active May 31, 2017 16:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nbremer/fa4a9105247ed1eeb5e3f44f14800893 to your computer and use it in GitHub Desktop.
Save nbremer/fa4a9105247ed1eeb5e3f44f14800893 to your computer and use it in GitHub Desktop.
Motion blur - Hexagon intro

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 hexagon was on the intro slide for the SVG filter Motion blur section during the presentation I gave at OpenVis 2016. The circles get blurred when they move and the faster they move, the more blurred I make them

This is to mimic the idea of motion blur that happens in real life

Other examples about motion blur are

Built with blockbuilder.org

<!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>
<style>
body {
text-align: center;
}
</style>
</head>
<body>
<div id="widthMeasure"></div>
<div id="hexagon"></div>
<script language="javascript" type="text/javascript">
///////////////////////////////////////////////////////////////////////////
//////////////////// Set up and initiate svg containers ///////////////////
///////////////////////////////////////////////////////////////////////////
var margin = {
top: 10,
right: 0,
bottom: 10,
left: 0
};
var width = document.getElementById('widthMeasure').offsetWidth - margin.left - margin.right - 10,
height = 400;
//SVG container
var svg = d3.select('#hexagon')
.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 + ")");
var motionDelay = 50,
motionDur = 900;
///////////////////////////////////////////////////////////////////////////
/////////////////////// Calculate hexagon variables ///////////////////////
///////////////////////////////////////////////////////////////////////////
var SQRT3 = Math.sqrt(3),
hexRadius = Math.min(width, height)/2,
hexWidth = SQRT3 * hexRadius,
hexHeight = 2 * hexRadius;
var hexagonPoly = [[0,-1],[SQRT3/2,0.5],[0,1],[-SQRT3/2,0.5],[-SQRT3/2,-0.5],[0,-1],[SQRT3/2,-0.5]];
var hexagonPath = "m" + hexagonPoly.map(function(p){ return [p[0]*hexRadius, p[1]*hexRadius].join(','); }).join('l') + "z";
///////////////////////////////////////////////////////////////////////////
//////////////////////////////// Create Data //////////////////////////////
///////////////////////////////////////////////////////////////////////////
//Create dataset with random initial positions
var randStart = [],
numCircles = 50,
radius = (hexHeight / numCircles)/2 * 0.98, //radius of each circle
maxMove = hexWidth/2 - radius; //maximum displacement
for(var i = 0; i < (numCircles-2); i++) {
//y location of each circle
var h = -hexHeight/2 + (i+1) * hexHeight/(numCircles-1);
//Find the x location that belongs to the height
if (h <= -hexHeight/4) {
var w = -1*(h + hexRadius)*SQRT3 + 2.6*radius;
} else if(h > -hexHeight/4 && h < hexHeight/4) {
var w = -hexWidth/2 + 1.4*radius;
} else if (h >= hexHeight/4) {
var w = (h - hexRadius)*SQRT3 + 2.6*radius;;
}//else if
randStart.push({
id: i,
yLoc: h,
xLoc: w,
maxValue: -6 * w/maxMove //The extent of the blur depends on the distance to cross. Less distance is less blur
});
}//for i
//var colors = ['#261c21','#6a224c','#b3274d','#db5b2d','#eb9605'];
//var colors = ["#3FB8AF","#7FC7AF","#DAD8A7","#FF9E9D","#FF3D7F"];
var colors = ["#004358","#1F8A70","#BEDB39","#FFE11A","#FD7400"];
var colorScale = d3.scale.linear()
.domain(d3.range(0,randStart.length-1,(randStart.length-1)/colors.length))
.range(colors);
///////////////////////////////////////////////////////////////////////////
///////////////////////////// Create gradient /////////////////////////////
///////////////////////////////////////////////////////////////////////////
//SVG filter for the fuzzy effect
//Code based on http://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("filter")
.data(randStart)
.enter().append("filter")
.attr("id",function(d,i) { return "fuzzy-" + d.id; })
.attr("width", "600%") //increase the width of the filter region to remove blur "boundary"
.attr("x", "-250%") //make sure the center of the "width" lies in the middle
.attr("color-interpolation-filters","sRGB") //to fix safari: http://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 a clip path that is the same as the top hexagon
svg.append("clipPath")
.attr("id", "clip")
.append("path")
.attr("d", "M" + (width/2) + "," + (height/2) + hexagonPath);
///////////////////////////////////////////////////////////////////////////
////////////////////// Place circles inside hexagon ///////////////////////
///////////////////////////////////////////////////////////////////////////
//First append a group for the clip path, then a new group that can be transformed
var circleWrapperClip = svg.append("g")
.attr("clip-path", "url(#clip")
.style("clip-path", "url(#clip)") //make it work in safari
.append("g")
.attr("transform", "translate(" + (width/2) + "," + (height/2) + ")");
//Outer group wrapper for a fixed y location
var circleWrapper = circleWrapperClip.selectAll(".circleWrapper")
.data(randStart)
.enter().append("g")
.attr("class", ".circleWrapper")
.attr("transform", function(d) { return "translate(" + 0 + "," + d.yLoc + ")" ;})
.style("opacity", 0);
//Finally append the visible circles
var circles = circleWrapper.append("circle")
.attr("class", "circles")
.attr("cx", function(d) { return d.xLoc; })
.attr("r", 0)
.style("fill", function(d,i) { return colorScale(i); })
.style("filter", function(d,i) { return "url(#fuzzy-" + d.id + ")"; })
///////////////////////////////////////////////////////////////////////////
///////////////////////// Place Hexagon in center /////////////////////////
///////////////////////////////////////////////////////////////////////////
//Place a hexagon on the scene
svg.append("path")
.attr("class", "hexagon")
.attr("d", "M" + (width/2) + "," + (height/2) + hexagonPath)
.style("stroke", "#F2F2F2")
.style("stroke-width", "4px")
.style("fill", "none");
///////////////////////////////////////////////////////////////////////////
////////////////////////// Starting transitions ///////////////////////////
///////////////////////////////////////////////////////////////////////////
//Better overlap with colored background of previous slide (spirograph)
circleWrapper.transition().duration(1500)
.style("opacity", 1);
//Let the circles grow at the start
circles.transition("grow").duration(motionDur)
.delay(function(d,i) { return i * motionDelay*2; })
.attr("r", radius);
//Initialize the chain of movement
circles.transition("move").duration(motionDur)
.delay(function(d,i) { return i * motionDelay; })
.each(slide);
///////////////////////////////////////////////////////////////////////////
////////////////////// Circle movement inside hexagon /////////////////////
///////////////////////////////////////////////////////////////////////////
//Function based on http://blockbuilder.org/mbostock/1125997
function slide(d) {
var circle = d3.select(this);
var element = d;
(function repeat() {
//Move the circles left and right
circle = circle.transition().duration(motionDur)
.attr("cx", -1*element.xLoc)
.transition().duration(motionDur)
.attr("cx", element.xLoc)
.each("end", repeat);
//Interpolate the fuzzyness
d3.selectAll("#fuzzy-" + element.id + " .blurValues")
.transition().duration(motionDur*0.3) //Move right
.delay(200)
.attrTween("stdDeviation", function() { return d3.interpolateString("0 0", element.maxValue+" 0"); })
.transition().duration(motionDur*0.3)
.attrTween("stdDeviation", function() { return d3.interpolateString(element.maxValue + " 0", "0 0"); })
.transition().duration(motionDur*0.3) //Move left
.delay(motionDur + 200)
.attrTween("stdDeviation", function() { return d3.interpolateString("0 0", element.maxValue+" 0"); })
.transition().duration(motionDur*0.3)
.attrTween("stdDeviation", function() { return d3.interpolateString(element.maxValue + " 0", "0 0"); });
})();
}//slide
</script>
</body>
</html>
@ongunsavas
Copy link

Hello, I am playing around with this javascript code to learn and I was wondering if there is a way to remove the clip path (circleWrapperClip) so it does not only render inside the hexagon, but also outside, or just make the clip path a square? Help is much appreciated!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment