Quick and dirty animaton with async/await

Animation is a pain if you do not know how it is implemented. Basically, you are updating a value with respect to time. This is generally done by calculating the time elapsed since the last frame and interpolating this value between its initial and final values. The interpolation needn’t be linear, there are many such interpolating functions with for example some easing. Even there are methods that will consider previous and later values such as Catmull-Rom splines. In this article, I’ll not go into the details but just introduce a simple ‘hack’ if you will.

This approach is influenced mainly from Unity coroutines with the yield keyword. Similarly,with the help of new ECMAscript features, namely async functions and await, we can define an animate function as such;

const startValue = 0; // start value
const endValue = 100; // end value
const totalTime = 1000; // Aniation time in milliseconds

let value = startValue;
async function animate() {
    const lastTime = performance.now();
    while (value <= endValue) {
        const time = performance.now() - lastTime;
        const ratio = time / totalTime;
        value = startValue + ratio * endValue;
        elementToAnimate.style.left = value + 'px';
        await new Promise(function(resolve, reject) {
            requestAnimationFrame(resolve);
        });
    }
    elementToAnimate.style.left = endValue + 'px';
}
animate();

The animate function will move elementToAnimate from 0 to 100 in one second (1000 milliseconds). The funtion effectively converts requestAnimationFrame into a Promise and waits for it in a while loop until value reaches the target value. Once the await line is hit, the javascript event loop will continue with other tasks until the animation frame is ready. When it is ready, the while loop continues from where it has left and calculates a new ratio according to the elapsed time and updates the value. Here, we can also pass ratio from an easing function to change the visible behaviour of the animation. When elapsed time exceeds our limit, we already know we should move on to the target value.

Although this is a nice and easy to use method for quickly animating things, I would not suggest using this for every case as it will not batch anything and animations around your code will queue many animation routines if you are using many, deteriorating performance. If you need to animate many things synchronously, it is better to use a similar loop that will update items at once rather than distributing them all over the place.

© Ali Naci Erdem 2024