CSS Animations aren’t that tough.

CSS Animations aren’t that tough.

January 13, 2015

Animations in CSS really aren't too tough and creating basic animations are almost down right simple. Plus they are totally awesome and fun.

CSS Animations are getting very good support in modern browsers starting with IE10, which means spending some time crafting a good animation will be more than just an Easter Egg for a small minority of your visitors. Most likely the majority of your audience will see animations. Even still a little progressive enhancement will go a long way for those that don’t get the animation. Did I say that animations are awesome and fun yet?

There are several animation properties that work together:

We’ll go over all these properties as well as the shorthand, but first let’s say we have a Striped Bass we want to have jumping out of the water. First off, I want to visualize the animation ahead of time or draw it out.

animation-sketch

 

So I basically want the Striped Bass to move from right to left with an arc towards the center, simulating a swimming and jumping motion. Shouldn’t be too tough. Here’s the HTML starting point, which is just basically an image inside of a figure tag.

<figure class="striper">
  <img src="/images/striper.png" alt="Elusive Striped Bass">
</figure>

Here’s the starting point for ourĀ css to get the fish at the right size in the middle of the page.

body {
  overflow: hidden;
}
img {
  max-width: 100%;
}
.striper {
  width: 200px;
  position: absolute;
  left: 50%;
  margin-left: -100px;
  bottom: 40px;
}

.striper is the class on the figure element where I set the width to 200px, which is the container to the actual image of the fish. Because the max-width property is set to 100% on the img tag this actually shrinks the fish making sure it is never larger than its container. The container is also absolutely positioned directly in the middle of the page using an old centering trick for absolutely positioned elements. You use left: 50% and then offset that with a margin-left of half the element’s size. So I used -100px.

Now for the fun part, animating the swimming motion. Animations have two parts. the animation properties and @keyframes. We’ll start first by setting up the animation properties.

Animation Properties

The first two properties that are critical are animation-name and animation-duration, the rest are optional.

animation-name

The animation name defines the animation. This can be whatever you want except the keyword of none which essentially turns it off.

.swim {
  -webkit-animation-name: swim;
}

I’m only going to use the -webkit- vendor prefix in my examples, but assume that you should be also using -moz-, -o-, and the unprefixed version on your production sites. Also, I’m using .swim as the class which I’ll add to the figure element along with the .striper class. My animation will be tied to the “swim” class to modularize my css a little.

<figure class="striper swim">
  <img src="/images/striper.png" alt="Elusive Striped Bass">
</figure>

animation-duration

This is simply a duration of time. Along with animation-name, its the only property that is required.

.swim {
  -webkit-animation-name: swim;
  -webkit-animation-duration: 3s;
}

I’ll use 3 seconds as the duration. The duration can be specified in seconds or ms, milliseconds. Normally I’m using seconds and if less than a second, I can use a decimal like .25s.

@keyframes

@keyframes are different than any other CSS rule set. It’s almost more like a media query than anything else, except you provide the name of the animation instead of the max-width or something. I’m using “swim” because that’s what I named the animation in the animation-name property. The animation name and what you put in the @keyframe have to be the same.

@-webkit-keyframes swim {
  from {-webkit-transform: translate(200%, 0)}
  to {-webkit-transform: translate(-200%, 0)}
}

A quick note that @keyframes must also be vendor prefixed, with @-webkit-, @-moz-, and @-o-, along with the non-prefixed version. Again, I’m only e using the @-webkit- version in my examples.

Inside the keyframe, there are two declaration blocks, from and to, the start and end of the animation. In my case I’m using the translate function of the transform property to move the fish -200% to 200%. Transforms are suited very well for animations for a few reasons:

Here’s a live example of our swmimming fish. You probably have to click the “rerun” button inside of the Codepen to see the animation as the animation already ran when the page initially loaded. In fact you’ll have to do this for all the Codepen examples.

See the Pen Animation 1 – Basic by Rich Finelli (@richfinelli) on CodePen.

Animation accomplished, but not very life-like. I want the fish to actually arc in the middle of the jump and rotate. So in order to create intermediate steps in the animation I have to do away with the from and to states and replace them with percentages.

@-webkit-keyframes swim {
  0% {-webkit-transform: translate(200%, 75%) rotate(0deg)}
  20%{ -webkit-transform: translate(100%, -55%) rotate(40deg)}
  40%{ -webkit-transform: translate(0%, -125%) rotate(-20deg)}
  60%{ -webkit-transform: translate(-100%, -55%) rotate(-30deg)}
  80% {-webkit-transform: translate(-200%, 75%) rotate(-50deg)}
  100% {-webkit-transform: translate(-300%, 200%) rotate(-50deg)}
}

I now have 6 steps, 0, 20, 40, 60, 80, and 100 percent. You can set as many as you like, as long as you have a 0% and 100%. Inside each step I’ve added a second value to the translate function, which controls the height and will provide the arc in the center of the fish’s swimming path. I’ve also got a second function value for the transform property, rotate which causes our fish to turn slightly to make it look slightly more life-like. Here’ the updated Codepen (remember to hit “rerun” to view it):

See the Pen Animation 2 – Percentage States by Rich Finelli (@richfinelli) on CodePen.

More Animation Properties

Better, but the animation is jumpy and when it completes the fish is brought back to the center of the frame, which is its position based on our non-animation related CSS. Let’s start by fixing the jumpy-ness.

animation-timing-function

The jumpy-ness of the animation is because the default value for the animation-timing-function is ease. And The animation-timing-function is kind of the same timing function that is used with transitions. The timing function basically allows you to adjust the speed of the animation over the duration of the animation. Well sort of, the animation timing function applies over the duration of each key frame. So each key frame is being “eased” so to speak. We need to go with a more linear approach:

.swim {
  -webkit-animation-name: swim;
  -webkit-animation-duration: 3s;
  -webkit-animation-timing-function: linear;
}

Using the linear animation-timing-function will keep the transition smooth.

See the Pen Animation 3 – Timing Function by Rich Finelli (@richfinelli) on CodePen.

animation-fill-mode

This is one of the most useful properties for animations, but it takes a second to wrap your head around it. The possible values are none, forwards, backwards, and both. The animation-fill-mode decides what to do with the element you are animating before the animation starts and after the animation finishes. Using backwards means that the element being animation will be positioned where the animation begins even before the animation starts. Using the forwards value will say that the animation will be positioned where the animation ends even once the animation is over. Told you this is somewhat confusing. This might help. Without this property, the element being animated will return back to its non-animated position after the animation ends, which in our case is the middle of the page.

The forwards property is very useful if you’re animation doesn’t end where the default position of the element is located. Backwards is particularly useful with the animation-delay property is being used, which we’ll get to in just a bit. I’m going to use the both value.

.swim {
  -webkit-animation-name: swim;
  -webkit-animation-duration: 3s;
  -webkit-animation-timing-function: linear;
  -webkit-animation-fill-mode: both;
}

See the Pen Animation 4 – fill-mode by Rich Finelli (@richfinelli) on CodePen.

After the aninimation concluded the fish was left at it’s position when the animation ended, and before the animation started, the fish was placed in the animation start position, which is a little less noticeable but would have been much more noticeable if we used the animation-delay.

animation-delay

This is pretty self explanatory. The animation-delay property delays the animation based on a number of seconds or milliseconds before the animation starts.

.swim {
  -webkit-animation-name: swim;
  -webkit-animation-duration: 3s;
  -webkit-animation-timing-function: linear;
  -webkit-animation-fill-mode: both;
  -webkit-animation-delay: 5s;
}

Notice below that the fish sits in the starting position for 5 seconds before the animation starts. The fact that the fish is in the starting position and not in the middle of the page is because the animation-fill-mode is set to both (forwards and backwards).

See the Pen Animation 5 – delay by Rich Finelli (@richfinelli) on CodePen.

animation-iteration-count

The iteration-count determines how many times the animation will run in succession. The default value is 1. Particularly useful is the infinite keyword.

.swim {
  -webkit-animation-name: swim;
  -webkit-animation-duration: 3s;
  -webkit-animation-timing-function: linear;
  -webkit-animation-delay: 5s;
  -webkit-animation-fill-mode: both;
  -webkit-animation-iteration-count: infinite;
}

See the Pen Animation 6 – iteration-count by Rich Finelli (@richfinelli) on CodePen.

The fish is now continuously jumping.

animation-play-state

The possible values are paused and running with the default value being running. This could be useful to add a play and pause button and allow jQuery to toggle a class that sets the running/paused values. Or we can add the paused value on :hover which is what we’ll do.

.swim {
  -webkit-animation-name: swim;
  -webkit-animation-duration: 3s;
  -webkit-animation-timing-function: linear;
  -webkit-animation-delay: 5s;
  -webkit-animation-fill-mode: both;
  -webkit-animation-iteration-count: infinite;
  -webkit-animation-play-state: running;
}
.swim:hover {
  -webkit-animation-play-state: paused;
}

See the Pen Animation 7 – play-state by Rich Finelli (@richfinelli) on CodePen.

animation-direction

This will control if the animation happens from 0% to 100% or from 100% to 0%. There are basically three values: normal, reverse, and alternate with normal being the default value. Reverse is going to make the animation happen from 100% to 0%, aka the reverse order.

.swim {
  -webkit-animation-name: swim;
  -webkit-animation-duration: 3s;
  -webkit-animation-timing-function: linear;
  -webkit-animation-delay: 5s;
  -webkit-animation-fill-mode: both;
  -webkit-animation-iteration-count: infinite;
  -webkit-animation-play-state: running;
  -webkit-animation-direction: alternate;
}
.swim:hover {
  -webkit-animation-play-state: paused;
}

See the Pen Animation 7 – direction by Rich Finelli (@richfinelli) on CodePen.

So the alternating animation-direction is odd in this context, but there are situations where it is very much needed. In the full animation below, I’m using just about every animation property except for the play state. Notice the fishing lure that is dropping and raising back up is using the animation-direction or alternate, and which is a better use of it than my previous example.

See the Pen Animation Final – Striped Bass by Rich Finelli (@richfinelli) on CodePen.

Animation Shorthand

Since going back and adding vendor prefixes is going to be a major pain, it’s nice to shorten up all of these animation properties in to one shorthand property.

.swim {
  -webkit-animation: swim 3s linear both 2s infinite;
}

The order of the properties doesn’t matter much, except I would always start with the animation-name and make sure the animation-duration comes before the animation-delay as the first time value will be assigned as the duration. Once you are using the shorthand it’s a lot easier to add the -moz-, -o-, and non-prefixed versions. Also be sure to do the same for the @keyframes as well.

Not so bad, right?

Besides dealing with the vendor prefixes, which can be handled through a Sass mixin, or by using another automation tool, animations are fairly easy and totally fun.

Mastering CSS: Book by Rich Finelli
Mastering CSS, the Book! Flexbox, Layout, Animations, Responsive, Retina, and more!
Mastering CSS, 2nd Edition, video course by Rich Finelli
Mastering CSS, Second Edition: 47 videos on how to make websites like a boss! Flexbox, Animations, Responsive, Retina, and more!
Back to top