Jumps: The New Steps() in Web Animation
This article discusses new easing options which are, as of the writing, available only in Firefox 65+. Demos will be fully realized only in Firefox until the other browsers implement this.
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
How do these new values compare to what we already had, and when are the best times to use each one?
Easings and the
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
animation, or Web Animations API animation completes over time.
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.
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 —
Starts and Ends
Instead of diving into what
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
jump prefix helps us explain the words start and end more effectively. When we use
jump-start we are telling the calculation to skip the starting position. With
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.
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?
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 (
360deg), and an easing of
steps(60). This gives us a clock with a second hand that steps to each mark on the clock (
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.
Another important reason is animating with sprites. You can have a strip of frames to animate and transform the position from
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.
This keeps us from having to do extra math to prevent the final (blank) frame from showing.
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
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
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.
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 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
0. With jumping
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
We’ve jumped starts, we’ve jumped ends, and we’ve jumped neither one, which leaves us with jumping both.
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.
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.