03 Spacing 03 Spacing 03 Spacing 03 Spacing
The key idea of this course: easing is spacing.
Chapter 2 fixed how long a motion takes. Spacing is where the object is on each frame in between: the distance it moves from one frame to the next. Two motions with the same timing can feel completely different, and spacing is the whole difference.
Animators plan spacing with a spacing chart (also called a tick chart): a line with a tick for every drawing. Close ticks mean the object barely moves between frames, so it looks slow. Wide ticks mean it jumps, so it looks fast. An easing is a curve that maps progress in time to progress in distance, which makes it a spacing plan written as maths. In practice: halfway through the time, how far along is the object? Linear says half. An ease-out says much more than half, because it front-loads the movement. Change that answer at every moment and you have changed the spacing.
Same timing, four spacings
Each row takes the same time over the same distance. Hover the figure to see every frame at once, as ghosts: faint copies of the object, one per frame, like the faint earlier drawings an animator sees through the paper. Compare the gaps between ghosts along each row, not the rows’ speed: they all arrive together. Only the spacing differs.
- Linear: even gaps. It moves like a machine, because only machines keep a constant speed.
- Ease-in: tight gaps, then wide. It starts slowly and ends fast.
- Ease-out: wide gaps, then tight. It starts fast and then settles.
- Ease-in-out: tight, wide, tight. It starts gently, travels, and lands gently.
The figure draws the three eased rows (the three that are not linear) with slightly stronger curves than the CSS keywords ease-in, ease-out and ease-in-out, so the differences are easy to see. The shapes are the same.
Which one arrives?
Play both cards a couple of times. Watch the last few frames of each.
A card comes onto the screen. Which one feels like it arrived?
Enter with ease-out, exit with ease-in
The rule follows from the world. Things arrive and settle; things leave and accelerate. An entrance should decelerate into place (ease-out). An exit should accelerate away (ease-in), and it should be quicker, because the thing that is leaving no longer needs your attention. (How much quicker is taste; 0.6 to 0.8 of the enter time is a sensible range, and this app uses 0.7.) On-screen moves, where something starts and ends in view, get both (ease-in-out).
This app’s own curves follow it: --ease-out is cubic-bezier(.2, .8, .2, 1) for enters, --ease-in is cubic-bezier(.4, 0, 1, 1) for exits, run at 0.7× the enter time (so a 280ms enter has a 196ms exit), and --ease-inout for moves. The next section explains what those four numbers mean.
Reading a cubic-bezier
cubic-bezier(x1, y1, x2, y2) draws a curve on a square from the bottom-left corner (start) to the top-right (end). Time runs across, from 0 to 1 (0 is the first frame, 1 the last). Progress runs up, from 0 (not started) to 1 (arrived). The four numbers are the positions of two handles, each a point on that square, one pulling on each end of the curve:
- The first handle
(x1, y1)sets the direction the curve leaves the start. Draw a line from the start corner to it: that line’s slope is the starting speed. - The second handle
(x2, y2)sets the direction it arrives at the end. Draw a line from it to the end corner: that line’s slope is the arriving speed.
A steep slope is fast. A flat slope is slow. So in cubic-bezier(.2, .8, .2, 1) the first handle is at (0.2, 0.8), and the line to it climbs 0.8 for every 0.2 across: steep, so the object leaves fast. The second handle is at (0.2, 1), level with the end corner, so the line from it to the end is flat: the object arrives slowly. The x values must stay between 0 and 1, since time cannot run backwards. A y above 1 (or below 0) makes the object overshoot its target and come back.
In the figure, the left graph is position against time, and the right is speed (the slope of the left one). Read the velocity graph on the right: --ease-out starts at four times the speed of linear motion (0.8 ÷ 0.2, the slope of its first handle) and is already about 95% of the way there at the halfway point, so it spends the second half of its time barely moving. That long, slow tail is the “settle”. The spacing chart shows the same thing: the ghosts crowd together at the end.
Two more spacings that CSS can write are worth knowing. steps(n) holds still and then jumps, so every gap is either zero or large: good for clocks, counters, and sprite sheets. linear() takes any list of stops, so it can draw spacing that no bézier can, like a bounce or a spring. It works in all current browsers, but older ones ignore it, so declare a plain transition before it as a fallback.
Drag the handles
Start from the CSS keyword ease-out. Its first handle sits in the bottom-left corner; its second lies flat along the top, and that flat handle is the settle. Pull the second handle straight down onto the dotted diagonal. Watch the velocity peak shrink to 1× and the ghosts spread evenly. You’re turning an arrival back into a machine. Then push the second handle above the top edge (a y over 1) and watch the object overshoot and return. B is ease-in, the spacing of an exit, kept as a reference: the reverse of an ease-out, slow start and fast finish.
The notation
/* Enter: decelerate into place. Exit: accelerate away, faster. */
.card-enter {
/* 280ms is --dur-base. The fade is shorter (180ms, --dur-quick) so the card is
fully visible while its slide is still settling. */
transition: transform 280ms cubic-bezier(.2, .8, .2, 1),
opacity 180ms linear;
}
/* Exit times are the enter times x 0.7: 280 -> 196ms, 180 -> 126ms. */
.card-exit {
transition: transform 196ms cubic-bezier(.4, 0, 1, 1),
opacity 126ms linear;
} /* Hold, then jump: eight positions per turn. steps(8) splits the 800ms into eight
equal holds, so the spinner turns in eight visible jumps. */
.spinner { animation: spin 800ms steps(8) infinite; }
@keyframes spin { to { rotate: 1turn; } }
/* Any spacing you can draw, as stops: a small bounce. Each stop is
"progress time%": at 20% of the time the object is 0.36 of the way, at 40% it
reaches 1 (the target), then it falls back to 0.81, returns to 1, and settles. */
.drop {
transition: transform 600ms
linear(0, 0.36 20%, 1 40%, 0.81 55%, 1 70%, 0.95 85%, 1);
} // The same enter as .card-enter, using the Web Animations API from chapter 1.
// fill: 'both' applies the first keyframe before the animation starts and holds
// the last one after it ends.
card.animate(
[
{ transform: 'translateY(16px)', opacity: 0 },
{ transform: 'none', opacity: 1 },
],
{ duration: 280, easing: 'cubic-bezier(.2, .8, .2, 1)', fill: 'both' },
); Match the curve
Watch a motion and pick the curve that made it. Look at the start and the end: where is it slow? You now know what a curve decides. Chapter 4 uses it to make things feel heavy or light.
Which curve made that motion?Spacing: reading a curve as speed.
5 trials. Judge with your eyes first; the numbers come after. Three right in a row makes trials harder, a miss eases them.