Chapter 4. Working with CSS Timing Functions

So far, we’ve created our animations and transitions by specifying only a handful of things: the properties to animate, the initial and final property values, and the duration. The exact syntax was different depending on whether we were dealing with a CSS animation or a transition, but the general ingredients were the same. The end result was an animation.

In this chapter, we are going to add one more ingredient into the mix. We are going to kick things up a few notches by using something known as a timing function (also referred to as an easing function). In the following sections, you’re going to learn all about them!

What Is a Timing Function?

What a timing function is, or what it does, is a bit complicated to explain using just words. Before I confuse you greatly with the verbal explanation, take a look at the following example if you have a browser and internet connection handy: http://bit.ly/timing_functions.

What you see should look a little bit like the following:

As you can see in the video, there are three circles that start at the left, slide to the right, and return to where they started from. For all three of these circles, the key animation-related properties we’ve set are almost identical. They share the same duration, and the same properties are being changed by the same amount. You can observe that by noticing that the circles start and stop moving at the same time. Despite their similarities, though, the animation for each circle is obviously very different. What is going on here? How is this possible?

What’s going on (and causing the difference in how each circle animates) is the star of this chapter: the timing function. Each circle’s applied animation uses a different timing function to achieve the same goal of sliding the circles back and forth. So, let’s get back to our original question: What exactly is a timing function? A timing function is a function that alters the speed at which your properties animate.

For example, your timing function could specify that your property values change linearly with time (Figure 4-1).

Figure 4-1. Boring!

This will result in your properties changing at a constant speed. If you want your property values to change in a more realistic way, you could throw in a timing function that mimics a deceleration, as in Figure 4-2.

Figure 4-2. Much less boring!

In this case, the speed at which your property value changes slows down very rapidly. It’s kind of like hitting the brakes on your car and measuring the position before stopping. To summarize, there are four important things to keep in mind about timing functions:

  • They don’t change where your property values start.
  • They don’t change where your property values end.
  • They don’t change your animation’s duration.
  • They alter the speed at which your property values change.

Now, we can spend all day looking at timing functions and learning more about what they do. We aren’t going to do that. I have done that before, and it is actually really boring! Instead, let’s shift gears and look at how we can use these magical creatures (I mean…ingredients) in CSS.

Timing Functions in CSS

Despite how complicated timing functions seem, using them in CSS is pretty straightforward. The various timing functions you can use are:

  • ease
  • linear
  • ease-in
  • ease-out
  • ease-in-out
  • step-start
  • step-end
  • steps()
  • cubic-bezier()

You can specify these timing functions as part of defining your animation or transition, and we’ll look at the details of how exactly to do that in the following sections.

Timing Functions in CSS Animations

In a CSS animation, you can specify your timing function as part of the shorthand animation property, or by setting the animation-timing-function property directly. Here is a snippet of what the shorthand and longhand variants might look like:

/* shorthand */
#foo {
  animation: bobble 2s ease-in infinite;
}
 
/* longhand */
#somethingSomethingDarkSide {
  animation-name: deathstar;
  animation-duration: 25s;
  animation-iteration-count: 1;
  animation-timing-function: ease-out;
}

When you declare your timing function as part of the animation declaration, all of your keyframes will actually be affected by that timing function value. For greater control, you can specify your timing functions on each individual keyframe instead:

@keyframes bobble {
  0% {
    transform: translate3d(50px, 40px, 0px);
    animation-timing-function: ease-in;
  }
  50% {
    transform: translate3d(50px, 50px, 0px);
    animation-timing-function: ease-out;
  }
  100% {
    transform: translate3d(50px, 40px, 0px);
  }
}

When you declare timing functions on individual keyframes, it overrides any timing functions you may have set in the broader animation declaration. That is a good thing to know if you want to mix and match timing functions and have them live in different places.

One last thing to note is that the animation-timing-function declared in a keyframe only affects the path your animation will take from the keyframe it is declared on until the next keyframe. This means you can’t have an animation-timing-function declared on your end (aka 100%) keyframe because there is no “next keyframe.” If you do end up declaring a timing function on the end keyframe anyway, that timing function will simply be ignored…and your friends and family will probably make fun of you behind your back for it.

Timing Functions in CSS Transitions

Transitions are a bit easier to look at since we don’t have to worry about keyframes. Your timing function can only live inside the transition shorthand declaration or as part of the transition-timing-function property in the longhand world:

/* shorthand */
#bar {
  transition: transform .5s ease-in-out;
}
 
/* longhand */
#karmaKramer {
  transition-property: all;
  transition-duration: .5s;
  transition-timing-function: linear;
}

There really isn’t anything more to say. As CSS properties go, transitions are pretty easy to deal with!

Default Timing Function Values

Specifying a timing function as part of your animation or transition is optional. The reason is that every animation or transition you use has its timing-function property defined by default with a value of ease.

Meet the Timing Function Curve

Timing functions are very visual creatures. While we use them in terms of their CSS names (ease, ease-in, etc.), the way we’ll commonly run into them is visually through something known as a timing function curve (see Figure 4-3).

Figure 4-3. An example of a timing function curve

The timing function curve isn’t a generic representation for all timing functions. Each timing function has a very specific timing function curve associated with it. Figure 4-4 shows the timing function curves look like for the predefined CSS timing functions.

You can sorta see from Figure 4-4 how the different timing functions end up affecting how our animation runs.

Now, there are two timing functions that we did not include in Figure 4-4: steps and cubic-bezier. These timing functions are special (which is usually code for complicated)! We will look at each in more detail in the following sections.

Figure 4-4. What the other timing functions look like

The cubic-bezier Timing Function

The cubic-bezier timing function is the most awesome of the timing functions we have. The way it works is a little complicated. It takes four numbers as its argument:

.foo {
  transition: transform .5s cubic-bezier(.70, .35, .41, .78);
}

These four numbers define precisely how our timing function will affect the property that is getting animated. With the right numerical values, you can re-create all of our predefined timing functions (like ease, ease-in-out, etc.). Now, that’s not particularly interesting. What is really really interesting is the large variety of custom timing functions that you can create instead.

For example, with the cubic-bezier timing function you can create a timing function that looks like Figure 4-5.

Figure 4-5. This timing function goes beyond the 100% mark while running

What this timing function highlights is that your property value will animate a bit beyond its final target and then snap back! That is something you can’t do using the predefined timing functions. This is just the tip of the iceberg on the kinds of timing functions you can create.

Now, you are probably wondering how we figure out the four numbers to throw into the cubic-bezier timing function. It’s one thing to look at a timing function curve and make sense of what is going to happen. It is quite another to look at four boring numbers to make sense of the same things. Fortunately, we’ll never have to calculate the four numbers ourselves. There are a handful of online resources that allow us to visually define the timing function curve, which, in turn, generates the four numerical values that correspond to it.

My favorite of those online resources is Lea Verou’s cubic-bezier generator:

You can use this generator to easily create a timing function by defining the timing function curve, previewing what an animation using that timing function would look like, and getting the numerical values that you can plug into your markup. Pretty simple, right?

The step Timing Function

The last timing function we will look at affects the rate at which our properties change but isn’t technically a timing function. This “non-timing function thing” is known as a step timing function (see Figure 4-6).

Figure 4-6. The step timing function

What a step function does is pretty unique. It allows you to play back your animation in fixed intervals. For example, in the step function graph in Figure 4-6, your animated property progression starts at 0%. At the 50% mark, it jumps to 50%. At the end of the animation, your property progression reaches 100%. There is no smooth transition between the various frames or “steps.” The end result is a bit jagged.

In CSS, you can define the step function by using the appropriately named steps function:

.pictureContainer img {
  position: relative;
  top: 0px;
  transition: top 1s steps(2, start);
}

The steps function takes two arguments:

  • Number of steps
  • A value of start or end to specify whether the first step should occur at the beginning of the animation or whether the last step occurs when the animation ends

For example, if I want my animation to have five steps and have a step when the animation ends, my steps function declaration would look as follows:

.pictureContainer img {
  position: relative;
  top: 0px;
  transition: top 1s steps(5, end);
}

One thing to note is that, the more steps you specify, the smoother your animation will be. After all, think of an individual step as a frame of your animation. The more frames you have over the same duration, the smoother your final result will be. That same statement applies for steps as well.

Conclusion

The icing on your animation- or transition-flavored cake is the timing function. The type of timing function you specify determines how lifelike your animation will be. By default, you have a handful of built-in timing functions you can specify as part of the animation-timing-function or transition-timing-function properties for your CSS animations and transitions. Whatever you do, just don’t forget to specify a timing function! The default ease timing function isn’t a great substitute for some of the better ones you can use, and your animation or transition will never forgive you for it.

Also, before I forget, here is the full markup for the three sliding circles that you saw at the beginning of the chapter:

<style>
  .circle {
    width: 100px;
    height: 100px;
    border-radius: 50%;
    margin: 30px;
    animation: slide 5s infinite;
  }
  #circle1 {
    animation-timing-function: ease-in-out;
    background-color: #E84855;
  }
  #circle2 {
    animation-timing-function: linear;
    background-color: #0099FF;
  }
  #circle3 {
    animation-timing-function: cubic-bezier(0, 1, .76, 1.14);
    background-color: #FFCC00;
  }
  #container {
    width: 550px;
    background-color: #FFF;
    border: 3px #CCC dashed;
    border-radius: 10px;
    padding-top: 5px;
    padding-bottom: 5px;
    margin: 0 auto;
  }
  @keyframes slide {
    0% {
      transform: translate3d(0, 0, 0);
    }
    25% {
      transform: translate3d(380px, 0, 0);
    }
    50% {
      transform: translate3d(0, 0, 0);
    }
    100% {
      transform: translate3d(0, 0, 0);
    }
  }
</style>

<div id="container">
  <div class="circle" id="circle1"></div>
  <div class="circle" id="circle2"></div>
  <div class="circle" id="circle3"></div>
</div>

There is nothing crazy going on in this example, so I’ll leave you to it…and see you in the next chapter!

Get Creating Web Animations now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.