Animation
If you want to animate things, eg move an element across the screen, you could accomplish it using setTimeout
or setInterval
(see Timing). Animations done this way won't be great because the redrawing is not synchronised to when the browser refreshes visuals. The result will be a poorly-performing, "janky" animation.
There are two better approaches. The first is to use requestAnimationFrame
, the second is to use CSS/HTML Animations. Using the CSS/HTML animations are generally preferable because they offer higher performance and for most cases a better quality result. A third approach is using Web Animations, but we'll skip this here.
requestAnimationFrame
A common use requestAnimationFrame
is for animating CANVAS
element drawing.
Conceptually, requestAnimationFrame
is like setInterval
, but the browser calls it in an optimised manner according to the rendering of the page. Because it's meant for things that are manipulating visuals, it is smart enough to start and stop depending on whether the web page visible.
It's as simple as this:
const loop = () => {
// Code to run
// ...
requestAnimationFrame(loop); // Reschedule
}
requestAnimationFrame(loop);
The code you put in the function should be as lean as possible in order to not disrupt rendering. Keep in mind it will be called continuously and at a high rate.
If there's a chance your code throws an error, or you want to be able to exit out of the function earlier, requestAnimationFrame
won't be called, so the loop stops. A simple fix is a pattern like this:
const loop = () => {
// Your code to run
// ...
}
const runner = () => {
try {
loop();
} catch (e) console.log(e)
requestAnimationFrame(runner);
}
requestAnimationFrame(runner);
Now loop()
will keep running, even if an error is thrown during the process, and you can use return
to exit the function whenever you want.
Sliding box
Let's use this technique to smoothly move a box by 1px, stopping after moving it 1000px:
<div id="box">
BOX
</div>
#box {
background-color: whitesmoke;
padding: 1rem;
position: fixed;
left: 10px;
}
const loop = () => {
const el = document.getElementById('box');
if (el.offsetLeft >= 1000) return; // Stop at some point
el.style.left = (el.offsetLeft + 1) + "px"
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
Bouncing box
Rather than hard-coding 1000px, we can make it respond to the window dimensions, to bounce back and forth.
Since we want to keep track of a few variables associated with the animation, the scheduled function is wrapped in another function to keep it nice and self-contained. By passing in el
as a parameter, we can use the same function to animate other elements as well.
const loopAnimation = (el) => {
const speed = 2; // How many pixels to move with each update
let delta = speed; // Current movement
const loop = () => {
var elRect = el.getBoundingClientRect();
var newLeft = elRect.left + delta;
if (newLeft + elRect.width >= window.innerWidth) {
// Too far right, make it start going the other way
delta = -speed;
} else if (newLeft < 0) {
// Too far left, make it start going the other way
delta = speed;
}
el.style.left = newLeft + "px";
requestAnimationFrame(loop); // Repeat itself
}
// Start it off
requestAnimationFrame(loop);
}
loopAnimation(document.getElementById('box'));
Rotate around centre
In this example, we'll spin an element around the centre of the window. Two helper functions aren't shown here: getCenter
and rotate
, but you can see the details at the live demo.
loopAnimation = (el) => {
const origin = getCenter(el); // Use center of box as origin
// Set radius to be a third of window width or height, whatever is smallest (to keep it on-screen)
const radius = Math.min(window.innerWidth, window.innerHeight) / 3;
let angle = 0;
const loop = () => {
const pt = rotate(angle, radius, origin);
el.style.left = pt.left + "px";
el.style.top = pt.top + "px";
angle += 0.01;
if (angle >= 360) angle = 0;
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
}
loopAnimation(document.getElementById('box'));
More
- Learn Web Animation - Great guide for animation covering techniques in CSS and JS.