Timers

To schedule transitions, D3 uses timers. Even an immediate transition will start after a delay of 17ms.

Far from keeping timers all to itself, D3 lets us use timers so that we can take animation beyond the two-keyframe model of transition. If not familiar with the animation terminology, keyframes define the start or end of a smooth transition.

To create a timer, we use d3.timer(); it takes a function, a delay, and a starting mark. After the set delay (in milliseconds) from the mark, the function will be executed repeatedly until it returns true. The mark should be a date converted into milliseconds since Unix epoch (Date.getTime() will do), or you can let D3 use Date.now() by default.

Let's animate the drawing of a parametric function to work just like the Spirograph toy you might have had as a kid.

We'll create a timer, let it run for a few seconds, and use the millisecond mark as the parameter for a parametric function.

Pass false to easingChart() in chapter5/index.js and add the following to the top:

import spirograph from './spirograph'; 
spirograph(true);

Next, create a new file in lib/chapter5/, called spirograph.js, and add this to the top:

import * as d3 from 'd3'; 
import chartFactory from '../common';

function spirograph(enabled) {
if (!enabled) return;

const chart = chartFactory();
}
export default spirograph;

We will construct a parametric equation. Here's a good function adapted from Wikipedia's article on those at http://en.wikipedia.org/wiki/Parametric_equations:

  const position = (t) => { 
const a = 80;
const b = 1;
const c = 1;
const d = 80;

return {
x: Math.cos(a * t) - Math.pow(Math.cos(b * t), 3),
y: Math.sin(c * t) - Math.pow(Math.sin(d * t), 3),
};
};

This function will return a mathematical position based on the parameter going from zero up. You can tweak the Spirograph by changing the a, b, c, and d variables; examples are there in the same Wikipedia article.

This function returns positions between -2 and 2, so we need some scales to make it visible on the screen:

  const tScale = d3.scaleLinear() 
.domain([500, 25000])
.range([0, 2 * Math.PI]);

const x = d3.scaleLinear()
.domain([-2, 2])
.range([100, chart.width - 100]);

const y = d3.scaleLinear()
.domain([-2, 2])
.range([chart.height - 100, 100]);

tScale will translate time into parameters for the function; x and y will calculate the final position of the image.

Now we need to define brush that flies around and draws the lines. We also need a variable to hold the previous position:

  const brush = chart.container.append('circle').attr('r', 4); 
let previous = position(0);

Next, we need to define an animation step function that moves the brush and draws a line between the previous and current points:

  const step = (time) => {
if (time > tScale.domain()[1]) {
return true;
}

const t = tScale(time);
const pos = position(t);

brush
.attr('cx', x(pos.x))
.attr('cy', y(pos.y));

chart.container.append('line')
.attr('x1', x(previous.x))
.attr('y1', y(previous.y))
.attr('x2', x(pos.x))
.attr('y2', y(pos.y))
.attr('stroke', 'steelblue')
.attr('stroke-width', 1.3);

previous = pos;
};

The first condition stops the timer when the current value of the time parameter is beyond the domain of tScale. Then, we use tScale() to translate the time to our parameter and get a new position for the brush.

Then, we move the brush--there is no transition because we are performing the transition ourselves already--and draw a new steelblue line between the previous and current position (pos).

We conclude by setting a new value for the previous position.

All that's left now is to create a timer:

d3.timer(step, 500);

That's it. Half a second after a page refresh, the code will begin drawing a shape and finish 25 seconds later.

After a while, it should look like this:

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset