4 min read

(For more resources related to this topic, see here.)

I can’t think of a better dragging demonstration than animating with the parallax illusion. The illusion works by having several keyframes rendered in vertical slices and dragging a screen over them to create an animated thingamabob.

Drawing the lines by hand would be tedious, so we’re using an image Marco Kuiper created in Photoshop. I asked on Twitter and he said we can use the image, if we check out his other work at marcofolio.net.

You can also get the image in the examples repository at https://raw.github.com/Swizec/d3.js-book-examples/master/ch4/parallax_base.png.

We need somewhere to put the parallax:

var width = 1200, height = 450, svg = d3.select('#graph') .append('svg') .attr({width: width, height: height});

We’ll use SVG’s native support for embedding bitmaps to insert parallax_base.png into the page:

svg.append('image') .attr({'xlink:href': 'parallax_base.png', width: width, height: height});

The image element’s magic stems from its xlink:href attribute. It understands links and even lets us embed images to create self-contained SVGs. To use that, you would prepend an image MIME type to a base64 encoded representation of the image.

For instance, the following line is the smallest embedded version of a spacer GIF. Don’t worry if you don’t know what a spacer GIF is; they were useful up to about 2005.

data:image/gif;base64,R0lGODlhAQABAID/ AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==

Anyway, now that we have the animation base, we need a screen that can be dragged. It’s going to be a bunch of carefully calibrated vertical lines:

var screen_width = 900, lines = d3.range(screen_width/6), x = d3.scale.ordinal().domain(lines).rangeBands([0, screen_width]);

We’ll base the screen off an array of numbers (lines). Since line thickness and density are very important, we divide screen_width by 6—five pixels for a line and one for spacing. Make sure the value of screen_width is a multiple of 6; otherwise anti-aliasing ruins the effect.

The x scale will help us place the lines evenly:

svg.append('g') .selectAll('line') .data(lines) .enter() .append('line') .style('shape-rendering', 'crispEdges') .attr({stroke: 'black', 'stroke-width': x.rangeBand()-1, x1: function (d) { return x(d); }, y1: 0, x2: function (d) { return x(d); }, y2: height});

There’s nothing particularly interesting here, just stuff you already know. The code goes through the array and draws a new vertical line for each entry. We made absolutely certain there won’t be any anti-aliasing by setting shape-rendering to crispEdges.

Time to define and activate a dragging behavior for our group of lines:

var drag = d3.behavior.drag()
.origin(Object)
.on('drag', function () {
d3.select(this)
.attr('transform', 'translate('+d3.event.x+', 0)')
.datum({x: d3.event.x, y: 0});
});

We created the behavior with d3.behavior.drag(), defined a .origin() accessor, and specified what happens on drag. The behavior automatically translates touch and mouse events to the higher-level drag event. How cool is that!

We need to give the behavior an origin so it knows how to calculate positions relatively; otherwise, the current position is always set to the mouse cursor and objects jump around. It’s terrible. Object is the identity function for elements and assumes a datum with x and y coordinates.

The heavy lifting happens inside the drag listener. We get the screen’s new position from d3.event.x, move the screen there, and update the attached .datum() method.

All that’s left to do is to call drag and make sure to set the attached datum to the current position:

svg.select('g') .datum({x: 0, y: 0}) .call(drag);

The item looks solid now! Try dragging the screen at different speeds.

The parallax effect doesn’t work very well on a retina display because the base image gets resized and our screen loses calibration.

Summary

In this article, we looked into the drag behavioud of d3. All this can be done by with just click events, but I heartily recommend d3’s behaviors module. It makes complex behaviors is that they automatically create relevant event listeners and let you work at a higher level of abstraction.

Resources for Article:


Further resources on this subject:


LEAVE A REPLY

Please enter your comment!
Please enter your name here