Skip to content
Tutorial

CSS Flip Book Animations Using a Simple SASS Mixin

A re-useable Sass mixin to create CSS animated sprite sheets at different frame rates
Comments
TLDR; How to animate a sprite sheet using CSS to create a classical or ‘flip book’ style animation. A simple SASS mixin can be easily configured to bulk-output multiple sprites at different durations and resolutions.

Below is an example of an SVG sprite sheet and a codepen to see it animated using the SASS Mixin. full code is at the bottom of the post

Running Tawa sprite sheet
Running Tawa sprite sheet

Animation basics

Inbetweening or 'tweening is the process of creating the in-between steps of an image that transform it from one state (keyframe) to another. Most of the time, when we see an animated design element on a webpage, the animation is created by a process where two or more keyframes are defined, and the computer generates the transitions between those states. This is true of simple animations, like creating fade-ins by transitioning from a zero opacity state to a one hundred percent opacity state, as well as complex animations, such as 3D animation in canvas with thousands of nodes being moved in space.

Frames per second (FPS)

We tend to think of computer animation as a fluid movement between point A and point B. In reality, the computer is calculating the graphic's state along the transition timeline and rendering it to the viewer as separate static images at a rate of 60 images per second. Because this is too fast for our brains to perceive the individual images, we interpret them as a single fluid transition. How many frames per second are needed for this optical illusion to take place? Surprisingly few. The human visual system can process between 10 and 12 images per second while still perceiving each image individually, more than this and we perceive them as motion.

Sixty transitions from point A to B vs twelve, why do more?
Sixty transitions from point A to B vs twelve, why do more?

Traditional animation (back in the days of film)

Through most of the history of animation, creating the in-betweens between keyframes was done manually. Animators would, by hand, redraw the same image, with incremental changes, as they transformed it from state A to state B. This was a hugely time-intensive process so obviously, they would want to redraw the fewest frames possible while still creating the illusion of movement which, as already established, is 12 frames per second (fps).

Back then, the only way to see animations was to show them using film projectors. Film projectors, however, ran at a rate of 24 fps. Animations at 12 fps would end up running at double speed. Since the animation would have to run at the same frame rate as the projectors, which was twice the frame rate required to simulate motion, each animation still was photographed twice so that an identical image would hold two consecutive frames – effectively doubling the frame rate without increasing the work.

In the cases where very fast movements were being animated, a higher frame rate is required for the appearance of smooth motion. This doubling of frames allowed for standard movements to be shoot with doubled frames (twos), and in cases of fast motion, to shoot in single frames (ones) and still have everything add up to the 24 frames per second on the film reel.

Early animation via sprite sheets

N64 Mario Sprite
N64 Mario Sprite

Early video games never felt "animated" but they still conveyed motion in a way that was able to run on the low-graphic-capability machines of the time. They represented motion via sprite sheets. Sprite sheets are a single image file with a graphic element in various poses. Essentially, it's all the point A and point B key-frames of an animation, without the intermediate frames, in a single image. The games would selectively show cropped portions of the image as the game state changed. Although the frame rate fell far short of one required for the illusion of movement, our brains were eager to ignore the gap.

Sprite sheets are still used today, but because of the huge increase in computer memory, they can be much larger and hold many more intermediate states — enough intermediate states that they effectively convey animation.

This sprite sheet for a Microraptor's run sequence has 29 frames
This sprite sheet for a Microraptor's run sequence has 29 frames

Create an animated sprite with CSS

Even with modern graphics cards and 'tweening capabilities, sometimes it’s still prudent to animate using a series of static images. Sprite sheets are still in common use in games, and if you’re going to be looping through a short sequence that involves many changing parts, the tradeoff in load time may be worth the performance gain in any web application.

Though an animated gif creates essentially the same thing (a low frame rate animation from a series of stills), creating a looping animation with a sprite sheet and CSS has the advantage that you can toggle play/pause states as easily as adding and removing a class. This can have practical applications, for example, in story-telling, in interfaces, and for accessibility requirements.

Sprite sheet mixin

I recently worked on a project that required making several animated graphics with sprite sheets in CSS. To simplify the process, I created a simple, reusable mixin that could easily handle animations of different dimensions and durations.

Tip: Sprite sheets can get pretty large, to keep the file size down I used SVGs.

Explaining the code

Changing just a few variables at the bottom, it generates two unique classes, one with a static image from the first frame of your sprite, and another with a looping animation.

The math involved is pretty simple and can be broken down into three main concepts:

  • Divide the width of the sprite sheet by the number of frames to calculate the width of each frame. Then force the containing div to maintain the correct aspect ratio by padding the bottom by the height dived by the frame width.
  • Set the background image of the div to be the sprite and make the image size to equal the number of frames times larger than the size of the div so that each frame is the full div width.
  • Break the animation into the number of steps that there are frames, and in each step, move the background to the left by an additional 100% of the div's width, or one frame.

Background sizing and position
Background sizing and position

SCSS mixin

.bg-sprite {
	width: 100%;
	height: 0;
	animation-timing-function: step-start;
	animation-iteration-count: infinite;
	background-repeat: repeat-x;
}

@mixin bg-sprite($name, $sprite-height, $sprite-width, $sprite-cell_count, $animation_duration, $sprite-url) {
	.bg-sprite--#{$name} {
		padding-bottom: percentage($sprite-height / ($sprite-width/$sprite-cell_count));
		background-image: url( #{$sprite-url} );
		background-size: $sprite-cell_count * 100%;
		transition: transform $animation_duration;
		background-position: 0% 50%;
	}
	.bg-sprite--#{$name}--playing {
		animation-name: animatedSprite-#{$name};
		animation-duration: $animation_duration;
	}
	@keyframes animatedSprite-#{$name} {
		@for $i from 0 through $sprite-cell_count {
			#{$i * (100% / $sprite-cell_count)} {
				background-position: (-100% * $i) 50%;
			}
		}
	}
}

$sprite-height: 220;
$sprite-width: 2048;
$sprite-cell_count: 9;
$animation_duration: 1s;

@include bg-sprite(
	'robin', 
	$sprite-height, 
	$sprite-width, 
	$sprite-cell_count, 
	$animation_duration, 
	'./sample_sprite--${name}.svg'
);

Efficiency through reusability

Making multiple sprite sheets? For reusability, I created a Sass function that can generate multiple animations each tied to it's their class name.

$avatar_list:	
	(tawa, 6, 500ms)
	(microraptor, 29, 1100ms)
	(robin, 9, 500ms)
	(future, 10, 400ms);

@each $name, $animationsteps, $speed, $url in $avatar_list {
	@include bg-sprite(
		$name,
		$sprite-height, 
		$sprite-width, 
		$sprite-cell_count, 
		'./sample_sprite--${name}.svg'
	);
}