Frame by Frame Animation Tutorial with CSS and JavaScript

Frame by frame animation with HTML, CSS and JavaScript

Imagine your designer asked you to implement a beautiful frame by frame animation for the awesome project you’ve been working on. As the front-end dev on the project, not only is it up to you to come up with a working animation, but also to deliver a silky smooth, performant and maintainable frame by frame animation that works great across different browsers, both on desktop and mobile.

This tutorial shows you the various ways you can create this type of animation with HTML, CSS, and JavaScript, while improving on each iteration to achieve the best result for your project.

What Is a Frame by Frame Animation?

According to this definition by Adobe, a frame by frame animation:

… changes the contents of the Stage in every frame. It is best suited to complex animation in which an image changes in every frame instead of simply moving across the Stage.

In other words, the subject of the animation is represented in a set of images. Each image in the set occupies an animation frame, and the rate of change with which each frame replaces the next produces the illusion of motion in the image.

I will demonstrate the whole workflow as you’ll be working on this great blinking eye animation from Zeiss website.

This is the set of images you’ll be using to build the animation frames:

Blinking eye sprite for frame by frame animation

And here’s the final result:

Blinking eye frame by frame animation

For this tutorial, I’ve chosen to use SVG images because they are great at scaling with different screen sizes for responsive web design. However, if for whatever reason you don’t want to use SVG graphics, you can create web animations with PNG, JPEG, and GIF image formats or use HTML5 Canvas. I will share my thoughts on these alternatives at the end of the article.

For simplicity’s sake, in this tutorial you’ll be using the jQuery library and have Autoprefixer up and running, therefore the code will be free of browser-specific CSS prefixes.

Now, let’s do some coding!

1 — Frame by Frame Animation by Changing the Image’s Source

The first option is straightforward enough, and that’s one reason why I like it.

In your HTML document, create an img element, which acts as container for the images, one at a time, as each animation frame is replaced by the next:

[code language=”html”]
<img class=”eye-animation”
src=”https//helpx.adobe.com/images/Eye-1.svg”
alt=”blinking eye animation”/>
[/code]

The CSS:

[code language=”css”]
.eye-animation {
width: 300px;
}
[/code]

The next step is to replace the existing image with the next one over a period of time dynamically, so as to create the illusion of movement.

You could do it with setTimeout but it’s better to use requestAnimationFrame. Doing so has some advantages from the point of view of performance. For example:

  • The quality of other animations on the screen won’t be affected
  • The browser will stop the animation if users navigate to another tab.

Here’s the jQuery code:

[code language=”js”]
const $element = $(‘.eye-animation’);
const imagePath = ‘/images’;
const totalFrames = 18;
const animationDuration = 1300;
const timePerFrame = animationDuration / totalFrames;
let timeWhenLastUpdate;
let timeFromLastUpdate;
let frameNumber = 1;

function step(startTime) {
if (!timeWhenLastUpdate) timeWhenLastUpdate = startTime;

timeFromLastUpdate = startTime – timeWhenLastUpdate;

if (timeFromLastUpdate > timePerFrame) {
$element.attr(‘src’, imagePath + `/Eye-${frameNumber}.svg`);
timeWhenLastUpdate = startTime;

if (frameNumber >= totalFrames) {
frameNumber = 1;
} else {
frameNumber = frameNumber + 1;
}
}

requestAnimationFrame(step);
}
[/code]

When the page loads, you call requestAnimationFrame for the first time and pass the step function to it, together with the animation’s startTime parameter built into the requestAnimationFrame function.

At each step the code checks how much time has passed from the last image source update and, if it’s longer than the required time per frame, then it refreshes the image.

Because you’re building an infinite animation, the code above checks if it’s got to the last frame and if so, it resets the frameNumber to 1; if not, it increases the frameNumber by 1.

Images should have the same name structure made of an increasing number series and the same location (e.g., images/Eye-1.svg, images/Eye-2.svg, images/Eye-3.svg, etc.) so that the code can easily iterate through them.

Finally, call requestAnimationFrame again to continue the whole process.

It looks good. However, if you try this you’ll see it won’t work because when the animation starts, only the first image gets loaded. The reason for this is that the browser knows nothing about the images you want to display until the code updates the image element’s src attribute in the middle of the animation loop. For a smooth animation, you need to preload the images before the loop starts.

There are different ways you can do this. Here’s the one I like best. It consists in appending hidden divs and setting their background-image property to point to the required images.

The jQuery code:

[code language=”js”]
$(document).ready(() => {
for (var i = 1; i < totalFrames + 1; i++) {
$(‘body’).append(`<div id=”preload-image-${i}” style=”background-image: url(‘${imagePath}/Eye-${i}.svg’);”></div>`);
}
});
[/code]

Here’s the full working demo on CodePen:

See the Pen Frame by frame-animation-1-source by SitePoint (@SitePoint) on CodePen.

Below I’ve listed some pros and cons to this approach.

Cons:

  • With HTTP v1.1, having to load multiple images can significantly increase page load time on the first visit
  • The animation can be janky on mobile devices. That’s because the browser has to perform a repaint each time the image element’s src attribute is updated (please check out Paul Lewis’ blog post for the details).

Pros:

  • Declarative — the code just iterates through the set of images
  • The image is fixed in one place — no jumping back and forth is visible (you’ll see why this is important below).

2 — Frame by Frame Animation by Changing the Image’s Opacity

To avoid browser repaints, you could change the images’ opacity instead of changing their source.

You could render all the images with opacity: 0 at page load and then set opacity: 1 for exactly when you want to show that frame.

That would improve rendering performance, but you’ll still have to preload all the images up front (which could be tricky if you have other images on the page as well and don’t want to wait for all of them to load). Also, because of multiple images, you’ll still have a longer first-time page load duration.

Here’s the full code:

You can avoid HTML code duplication by leveraging the capabilities of template engines such as Pug, Twig, React, Angular etc., or by just appending the divs using JavaScript as you did in the previous example.

3 — Frame by Frame Animation by Changing the Sprite’s Position

The usual workaround to prevent having to download multiple images consists in using an image sprite.

So, let’s do that.

Continue reading %Frame by Frame Animation Tutorial with CSS and JavaScript%


Source: Sitepoint