Ease does it

As soon as you try your luck with Zdog you might be tempted to dive in the exciting cosmos of animation. There are different approaches for this, and, hidden in the library, there is also space for intriguing changes.

Scene

It is helpful to have a scene, an example to show off the different techniques. With Zdog you create pseudo-3D graphics with canvas or SVG, and when it comes to animating shapes, it is advisable to pick the first option. In the body of an <svg> element shapes appear between the opening and closing tags, so that the browser would need to update the document tree over and over. With <canvas> the graphics appear in the 2D context, in a more performant environment.

<canvas width="400" height="400"></canvas>

With JavaScript, as soon as you target the specific node, you operate through the classes offered by the library. Immediately, you have Illustration.

const illustration = new Zdog.Illustration({
	element: 'canvas'
});

With an instance of the class you point to the canvas to create the base of the graphic. The idea is to then add instances of other classes on this reference. With a Shape class, for example, you draw a perfect sphere.

new Zdog.Shape({
	addTo: illustration,
	stroke: 20
});

The stroke property determines the size of the object, quite effectively describing the radius of the round figure. Add the object to the illustration and all you need to see the result is calling a method on the variable, updateRenderGraph.

illustration.updateRenderGraph();

Action

Say you want to animate the sphere to rotate around the center, similarly to a planet orbiting around an immovable body. First, you need to nudge the shape slightly to the side — by default, shapes are drawn from the center.

new Zdog.Shape({
	addTo: illustration,
	stroke: 20,
	translate: {
		x: 50
	}
});

Past the horizontal offset, the idea is to rotate the z axis for the entire illustration.

illustration.rotate.z = 0.5;
illustration.updateRenderGraph();

Once you rotate the illustration, and once you remember to update the graphic by repeating the updateRenderGraph method, the change is set. The sphere moves to the new point.

To animate the change, smoothly and over time, you can lean on requestAnimationFrame. Thanks to the API you are able to call a function over and over, at the convenience of the browser.

const animate = () => {
	// ...animate

	requestAnimationFrame(animate);
};

animate();

In the repeating function you then proceed to update the graphic, in angle and appearance.

const animate = () => {
	illustration.rotate.z = (illustration.rotate.z + 0.021) % Zdog.TAU;
	illustration.updateRenderGraph();

	// ...requestAnimation
};

Here I update the number of radians with an arbitrary amount and one small precaution. Indeed, the position of the shape follows the same pattern after a full rotation, so that it is possible to constrain the angle in between 0 and twice the value of PI. In other words, Zdog.TAU.

Call the function with requestAnimationFrame and the animation is complete. But while the code works, it also works in a linear fashion. How to deviate from the robotic standard? The library offers a way forward with an easing function, easeInOut, and the gist is surprisingly simple. This is a function which receives a number in the 0 to 1 range. Based on the input the function returns a new number, structured in a less-than-linear interval. With this in mind you can separate the logic in explicit variables.

let counter = 0;
const threshold = 300;

In the animate function increment the counter variable, limiting the value with the chosen threshold.

counter = (counter + 1) % threshold;

In the same scope, feed the division between the two to the easing function.

const ease = Zdog.easeInOut(counter / threshold);

In this manner you have an easing value, which you can then use to animate the graphic.

illustration.rotate.z = ease * Zdog.TAU;
illustration.updateRenderGraph();

By multiplying the easing with the value of TAU you achieve a full rotation, but this time the change is nuanced, to start and end more slowly. And this is not the end of the story. easeInOut accepts a second argument, detailing the strength of the easing.

const ease = Zdog.easeInOut(counter / threshold, 3);

Set the value to 3 and the change is even more pronounced. And for this matter, easeInOut is an easing function, and any function like it would work just as effectively. If you are familiar with Svelte you might nod in delight, knowing of the existence of the svelte/easing module. Here you can explore more exotic options like backInOut.

import { backInOut } from 'svelte/easing';

// ...
const ease = backInOut(counter / threshold);

Linear, eased. There are several ways to animate to the graphic, to add character to the shapes.

Be warned, however, that some functions tend to be excessively responsive — I’ll let you test bounceInOut on your own.