神刀安全网

Creating real-life based motion effects in D3.js visuals

A filter that can create a subtle but interesting effect has to do with motion blur. The faster and closer to us things move in the real world, the more blurred they appear. And with the right filter, you can recreate this effect on the screen as well. The circles moving in the hexagon below, for example, the faster they move, the more blurred I make them to mimic the feeling of fast movement.

I came across an example of a “motion filter” while seeing images being blurred while sliding across the screen in an image gallery demo . Since data visualization also often shows movement, I wanted to see what I could do with it.

Although the image below is just an animated gif. You can see the fully functioning version here

Creating real-life based motion effects in D3.js visuals

SVGs beyond mere shapes blog series

This blog is part of the SVGs beyond mere shapes blog series. It’s based on the talk I gave at OpenVis on April 25th, 2016 in Boston. My goal was to inspire people to experiment with the norm, to create new ways of making a visual more effective or fun. Even for a subject as narrow as SVG filters and gradients, there are more things possible than you might think. From SVG gradients that can be based on data, dynamic, animated gradients and more, to SVG filters for creating glow, gooey, and fuzzy effects. You can find links to all of the other blogs in my kick-off post here .

Creating real-life based motion effects in D3.js visuals

Setting up the filter

The essence of this filter is super simple. It’s not even a combination of filters, which seems to be more common, but just a Gaussian blur. The only caveat is that this blur is made in one direction (say, the horizontal direction) instead of over the entire shape equally. However, making it look good visually actually involves a few more steps.

Let’s start by setting up a simple filter. As in the gradient case, we have to nest a filter within a defs element. Then we can append a filter element and give it a unique ID to reference it later on.

Due to the nature of the blur we also have to make some changes to the region where the filter is “allowed” to bleed into. The blur will increase the width (or height) of the object’s appearance. Normally a filter can bleed into the region of 120% in the horizontal and vertical direction around the bounding box of the object (the smallest rectangle that can enclose the shape). With these motion blur effects, however, we will often need more than 120%. It depends on how much blur you apply, but values around 300% are more typical I find. Therefore, we increase the filter region with the width attribute. To make sure this region will still be centered in the middle of the object, we have to move it back a bit with the x attribute (whose default is -10%). You can read more about the filter region here .

Creating real-life based motion effects in D3.js visuals

Now you can start appending as many filter techniques as you want, but as said before, here we only need one: a feGaussianBlur . With the in attribute set to SourceGraphic, we mean that the filter should use the element onto which the filter is applied as the base (when chaining filters, you can also supply the result of a previous filter for example). Because we want to control the appearance of the amount of blur during motion I give the gaussian blur a class so I can easily select it later on. Finally, we set the amount of blur with stdDeviation . When you only supply one value, it does a blur in all directions. But when you supply 2 values, the first belongs to a blur along the x, or horizontal, direction and the second belongs to the y, or vertical, direction.

Finally, we can apply the filter to any SVG element by setting the filter style of the element to the filter’s unique ID with url (#filter-id)

  //Always start by appending a defs (definitions) element var defs = svg.append("defs");  //Initialize the filter defs.append("filter")  .attr("id", "motionFilter")  //Give it a unique ID  .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 of the element  .append("feGaussianBlur") //Append a filter technique  .attr("class", "blurValues") //Needed to select later on  .attr("in", "SourceGraphic") //Perform the blur on the applied element  .attr("stdDeviation", "8,0"); //Do a blur of 8 standard deviations in the           //horizontal direction and 0 in vertical  //Apply the filter to an element d3.select(".element").style("filter", "url(#motionFilter)");  

In the image below you see examples of applying a blur of 0, 2, 4, 6 and 8 stdDeviation in the vertical direction.

Creating real-life based motion effects in D3.js visuals

Note: For Safari you need to explicitly set the color-interpolation-filters attribute of the filter to sRGB , otherwise, the colors seem faded out (see this question on StackOverflow )

Change the filter during motion

The static image above was simple to create, but we only want to create a blur when something is moving. That means that we have to transition the stdDeviation value of the filter during a movement. Although d3 can do many transitions quite easily, it doesn’t know how to transition a stdDeviation attribute (and I don’t blame it). Luckily, the transition step only needs some help from the  attrTween method together with a custom interpolator.

There are two steps during a movement of an element, such as a circle:

  • The speed-up when we want the element to increase in blur
  • The slow-down when the element is stopping and the blur should go back to 0

Therefore, we have to first transition the stdDeviation of the filter from 0 to, say, 8 during the speed-up and afterwards, transition it back from 8 to 0 during the slow-down. Here is the code with two transition steps:

  //Interpolate the motion blur d3.select("#motionFilter .blurValues") //select the feGaussianBlur  //Step 1: transition the filter from 0 blur to a heavy blur - start of a circle movement  .transition().duration(300)  .delay(300)  .attrTween("stdDeviation", function(d,i) { //change the values of stdDeviation     //initialize an interpolator that runs from 0 to 8 in 300 milliseconds   var interpolate = d3.interpolate(0, 8);     //"value" will run from 0 to 8, the horizontal blur   return function(t) {    var value = interpolate(t);    return value + " 0";   };    })    //Step 2: transition the filter from heavy blur to a 0 blur - end of a circle movement    //besides the interpolator now going from 8 to 0 the rest is the same as above    .transition().duration(300)  .attrTween("stdDeviation", function(d,i) {   var interpolate = d3.interpolate(8, 0);   return function(t) {    var value = interpolate(t);    return value + " 0";   };    });  

Let me explain. First, select the feGaussianBlur  using the filter’s ID and the class we gave it. Start a transition as normal with the transition , duration and delay steps (I find that a small delay to the blur after a movement starts looks better). But instead of supplying an attribute such as opacity and its new value, we now tell the transition that we want to do something with the stdDeviation attribute using attrTween . What attrTween should return are the values that stdDeviation should become. For example, “0 0” at the start, to “1 0” a small amount of time later, to “8 0” at the end.

We first set up an interpolator that will run from the first number supplied to it, to the second number within the time given by the duration attribute and it will also take into account the easing function/shape (the default is circular). Finally, we return a function where value will be interpolated between 0 and 8. The function returns a string that consists of value + ” 0″ , which might look like “4 0” halfway, which is exactly what stdDeviation needs. (If stdDeviation only needed one number, this function in attrTween would be very simple. Then you only need to return the interpolator itself. See this example from Mike Bostock for example.)

And that was for the speed-up. However, for the slow-down we can just copy the whole transition and paste it again. We only have to change the interpolator to go from 8 to 0.

You do have to play around with the durations and delays of these two transitions when you apply them to your moving elements to see what looks best.

Below you can see the result of applying this to two circles. They are both undergoing the same motion blur at the same time, but only one of them is also moving while this happens.

For more information about the types of interpolators that already exist in d3, check out the d3 wiki page on Transitions . And here is another well-commented example of using attrTween by Mike Bostock.

Examples

Moving along an angle

The blur can only be performed in either a horizontal or vertical direction. But what if your objects are moving along an angle? Then you will have to rotate the elements so that either the new horizontal or vertical direction (in the rotated system) will lie along the angle. This might involve some math if the angles are different per each datapoint, so I’ll leave it up to you to decide if your data visualization is too complex to use with the motion blur.

Also, you can create a separate motion blur filter for each of your datapoints so that you can adjust each filter dynamically when the datapoints are not moving at the same time (such as in the hexagon example at the top). Or in the example below where I’ve created a somewhat exaggerated motion blur when the balls are flying outward and back into the center. Each circle has its own filter that gets pushed from a “0 0” stdDeviation to a heavy blur whenever the corresponding circle is moving.

Click on the image below to be taken to an example where circles are flying out from a center point one after another (or click here ). You can click anywhere in that SVG to activate or deactivate the motion blur filter.

Creating real-life based motion effects in D3.js visuals

Strangely enough, there seems to be some crispening artifact on the circles when the filter is applied (doesn’t matter which filter though). Not sure why this is, if any of you know how to fix this, I’d love to hear!

Top running speeds

For an actual data visualization example, I thought that visualizing the top running speeds of some animals and the fastest human seemed appropriate. When the circles fly out, they get blurred and the amount of blur depends on how fast they move (i.e. how far they have to move outward). To make this blur depend on the data, we only have to make a small adjustment to the interpolator. Pick a maximum blur that you want, say 12. Then find out which value is the maximum in the dataset (which will be the Cheetah in this case). The value that the interpolator should go to for each animal then depends on how much slower this animal goes in comparison with the Cheetah. See the lines below for the small changes:

  //Max speed in dataset (Cheetah) var maxSpeed = d3.max(animals, function(d) { return d.speed; }); //Max motion blur that I want var maxBlur = 12;  //Make the blur of each animal depend on its speed //So the slower they move, the smaller the blur  var interpolate = d3.interpolate(0, d.speed * maxBlur / maxSpeed);  

Don’t forget to adjust this interpolator in both the speed-up and slow-down. Once this is done, you get the result below which is a screenshot of the outward movement that also nicely shows the blur (although you don’t notice it that severely when they are moving, which is a nice thing :) ). Click on the image to go to the interactive version

Creating real-life based motion effects in D3.js visuals

The Code

The code for all the examples that were showcased in the blog can be found here:

I really enjoy the motion blur effect for its subtle dedication to details to make it more life-like I guess. If you’ve made any visualizations while using this motion blur effect, please share them in the comments below or let me know on Twitter, I’d love to see them!

SVGs beyond mere shapes

If you’re interested in seeing more examples of how SVG gradients or SVG filters can be used to make your data visualization more effective, engaging or fun, check out the other ±10 blogs that are coming up. You can find links to all of the blogs (they might nog all be live yet, I’m posting about 1 a week) in my kick-off post here

Creating real-life based motion effects in D3.js visuals

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » Creating real-life based motion effects in D3.js visuals

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址