A tutorial on how to create a motion blur effect on HTML elements using JavaScript and an SVG blur filter.
Today we are going to show you how to make a motion blur effect and apply it to regular JS or CSS animations of HTML elements.
Motion blur is a technique widely used in motion graphics and animation in general to make movement seem more smooth and natural.
Motion blur is the apparent streaking of rapidly moving objects in a still image or a sequence of images such as a movie or animation. It results when the image being recorded changes during the recording of a single frame, either due to rapid movement or long exposure.
In this article, we’ll take a look at how to make an approximation of that effect, for horizontal or vertical transitions.
Attention: Please keep in mind that this effect is highly experimental and only supported by some modern browsers. Chrome seems to have the best performance for it as of now.
In order to apply a motion blur effect to an animation, we need to apply a directional blur to the object according to the speed and direction it is moving, for every frame.
Let’s take a look at the steps we need to take to understand how the effect works:
Setting the blur
Since the regular CSS blur filter does not support directional blur, we are going to have to use SVG filters.
We’ve already covered the basics of SVG filters in the Creative Gooey Effects article.
For this effect, we will only be using the feGaussianBlur filter primitive.
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" class="filters">
<defs>
<filter id="blur">
<feGaussianBlur in="SourceGraphic" stdDeviation="0,0" />
</filter>
</defs>
</svg>
The stdDeviation attribute is used to set the blur intensity, and can take up to two parameters, for blur in the horizontal and vertical orientation.
Applying the filter to an element, as we’ve seen before, is simple enough:
.selector{
-webkit-filter: url("#blur");
filter: url("../index.html#blur");
}
For the motion blur effect, however, we are going to have to update the filter dynamically for every frame through JS.
First we will have to select and store the filter in a variable so that we can access it later. Since jQuery doesn’t play well with SVG elements, we need to select the element using native JS functions:
var filters = document.querySelector(".filters"), // the SVG that contains the filters
defs = filters.querySelector("defs"), // the element inside the SVG
blur = defs.querySelector("#blur"), // the blur filter
blurFilter = blur.firstElementChild; // the feGaussianBlur primitive
Setting the intensity then, is just a matter of changing the stdDeviation attribute of the filter primitive. For example, to set a horizontal 12px blur:
blurFilter.setAttribute("stdDeviation","12,0");
Keep in mind that this blur filter only supports directional blur on either the X or the Y direction, and not on any arbitrary angle, so you should plan your animations accordingly.
Note though, that changing the blur filter affects all objects linked to it, so we need a new <filter>
element for each object we want to apply this effect to. Here is a simple way of creating these filters dynamically:
// go through all the objects that need a blur filter
$(".js-blur").each(function(i){
// clone the filter
var blurClone=blur.cloneNode(true);
// create and set a new ID so we can use the filter through CSS
var blurId="blur"+i;
blurClone.setAttribute("id",blurId);
defs.appendChild(blurClone);
// set the CSS
var filter="url(#"+blurId+")";
$(this)
.css({
webkitFilter:filter,
filter:filter
})
// store the filter reference on the object for practicity
.data("blur",blurClone)
;
});
Measuring the speed
For the next step, we need to be able to calculate, how far the object has moved since the last frame. We need to do this for every frame. The method for achieving this might vary according to how everything is set up; how the animation is done, etc. In this tutorial, we’ll take a more generalist approach, which, while it might not be optimized for all use cases, should work with most JS and CSS animations.
To get the position, we’ll be using jQuery’s offset function, which is just what we need: it returns the element’s coordinates relative to the document (rather than its parent), and takes the transform property into account.
To be able to check for changes and update every frame, we’ll use requestAnimationFrame.
Here’s an example:
// the element we want to apply the effect
var $element=$(".selector");
// storing the last position, to be able to measure changes
var lastPos=$element.offset();
// a multiplier, to be able to control the intensity of the effect
var multiplier=0.25;
// a helper to simplify setting the blur.
function setBlur(x,y){
blurFilter.setAttribute("stdDeviation",x+","+y);
}
(function updateMotionBlur(){
// get the current position of the element
var currentPos=$element.offset();
// calculate the changes from the last frame and apply the multiplier
var xDiff=Math.abs(currentPos.left-lastPos.left)*multiplier;
var yDiff=Math.abs(currentPos.top-lastPos.top)*multiplier;
// set the blur
setBlur(xDiff,yDiff);
// store current position for the next frame
lastPos=currentPos;
// call to update in the next frame
requestAnimationFrame(updateMotionBlur);
})();
And here is the result:
This is the basic approach which takes only one element into consideration. A more complicated use might require code optimized for it in particular. For a more sophisticated take, you may look into applying the motion blur effect to multiple objects, disabling the blur and the speed calculation when there’s no animation, and so on.
And we’re done here! Again, with all things filter, please be aware that this effect can be resource intensive, so you should refrain from using it on large objects.