Jumps: The New Steps() in Web Animation

The first big change to web animation easings (aka timing functions) is upon us. After some discussions, updates to the specification, and initial implementations as a separate function, Firefox 65 introduces four new options for the steps() function.

  • jump-start
  • jump-end
  • jump-both
  • jump-none

See the Pen Visual Reference: Steps() and Other Easings, Translate by Dan Wilson (@danwilson) on CodePen.

How do these new values compare to what we already had, and when are the best times to use each one?

Easings and the steps() function 

First, let’s take a step back and discuss what easings even are and what the steps() function allows us to do.

Easings allow us to change how a transition, CSS animation, or Web Animations API animation completes over time.

.mover {
animation: move 2000ms;
animation-timing-function: linear; /* easing */
transform: translateX(0px);
}

@keyframes move {
100% {
transform: translateX(200px);
}
}

With a linear easing, everything moves at a steady pace. If we change that to ease-in we will have something that starts slower and speeds up as it gets to the end of its animation.

Steps are a bit different, as we can tell the animation to have a specific number of distinct frames. So changing the easing to steps(2), for example, would give an animation with only two states, a starting position and an ending one.

How steps() determines each step interval is based on a second (and optional) parameter. This is where our new values come into play and join the two existing values — start and end.

Starts and Ends 

Instead of diving into what start and end mean, let’s cut to the chase to say that two of the four aforementioned new values are in fact aliases of these original values:

  • jump-start === start
  • jump-end === end

The jump prefix helps us explain the words start and end more effectively. When we use start or jump-start we are telling the calculation to skip the starting position. With end/jump-end we want to skip the ending position.

You can think of the steps(n) function as taking snapshots of an animation with a linear easing at the specified intervals and displaying that snapshot until it is time to show the next snapshot. So when we say we want steps(4, jump-end) we will get an animation that divides our animation into four sections and snapshots the initial position of each of those fourths. With steps(5, jump-start) we get an animation divided into five parts, and we take the final position in each part.

See the Pen Visual Reference: Jump Start, Jump End, Linear by Dan Wilson (@danwilson) on CodePen.

Why would you want to skip a beginning or ending state? We are telling the browser to go from a specific state to a different specific state with our animation keyframes, so wouldn’t we always want both those states represented in our resulting animation?

Rotations 

It becomes clearer to realize the benefits of skipping a start or end when you think about the seconds hand on a clock — we’d probably want an animation running for 60 seconds for a full rotation (0deg to 360deg), and an easing of steps(60). This gives us a clock with a second hand that steps to each mark on the clock (jump-end/end is assumed when no second parameter is specified). Without the jumping of the end state, we would have an animation where the start and end would be at the top (0deg), and thus our clock would not be natural as it would stay at the top for two seconds and do the other 58 seconds in between.

See the Pen Visual Reference: Steps() and Other Easings, Full Rotation by Dan Wilson (@danwilson) on CodePen.

Sprites 

Another important reason is animating with sprites. You can have a strip of frames to animate and transform the position from translateX(0) to translate(-100%) (or use background positions, etc). If you translate a full 100% of the width, the final state will be out of view, so we again jump the end, and we can magically capture each frame.

See the Pen Visual Reference: Steps + Sprites by Dan Wilson (@danwilson) on CodePen.

This keeps us from having to do extra math to prevent the final (blank) frame from showing.

Jumping none 

Ah, yes. The new stuff.

Sometimes skipping a state really is not what we are looking for. The new option jump-none allows an animation that does not jump the start or the end. For any animation with a step count of at least two, the begining state and the ending state will be represented. The remaining steps will be distributed evenly between. Three steps will have their effective snapshots taken at 0%, 50%, and 100%.

Moving an object 

A straightforward case for this option is moving an object across a screen. We might want to move an object from point A to point B with a stepped effect. Previously with only the jumping of start or end available, there was not a straightforward way to tell the animation to show the starting and ending positions with equal frames in between. The addition of jump-none now gives us this ability.

See the Pen Visual Reference: jump-none vs linear easing by Dan Wilson (@danwilson) on CodePen.

With the old ways of steps(), you would usually still be able to achieve this, but you would need to do some extra math and make the translation technically go beyond your visual start or end state. Now it is more straightforward as you can be confident your start and end states are what you explicitly make them.

Opacity 

Opacity also can benefit from making sure the start and end states are always visible. Say we want to do a fade out via a stepped opacity animation from 1 to 0. With jumping start or end, either the fully opaque or the fully transparent state will never be seen. But jump-none make sure both are seen. An animation with steps(2, jump-none) will have a straight on/off animation (to create a clean strobe), and steps(4,jump-none) will give us opacities of 1, .6667, .3333 and 0.

See the Pen Visual Reference: Steps() with Opacity by Dan Wilson (@danwilson) on CodePen.

Jumping Both 

We’ve jumped starts, we’ve jumped ends, and we’ve jumped neither one, which leaves us with jumping both.

See the Pen Visual Reference: jump-both vs linear easing by Dan Wilson (@danwilson) on CodePen.

I will be interested to see the use cases for this animation. On the one hand it allows for completeness (since we are adding a none, we might as well add a both), but it also has potential as easing options might be used outside animation. The use cases for using easings in the context of gradients seem more compelling (to me, in the moment I write these words), than what jump-both provides in the context of animation.

Browser Support 

Chrome already implemented the jump-none behavior under the older frames() spec discussion, so I suspect it will not be a huge lift to move it into the new naming. Webkit and EdgeHTML do not have it in any preview versions yet. So it is time to familiarize and experiment rather than go all in without fallbacks.