Previous Lecture Complete and continue  

  Animating the background

Animating the background

In this lesson we're going to start bringing some animation to the "Hero Header" section of our landing page. By the end of this lesson, you'll know how to set up and animate the background image, as well as how to make sure the animation looks great.

Getting started

Open the sample code zip file and open folder 00-start. A completed version of this lesson's code is in the folder 01-animated-background.

In this you'll find there's an index.html file, containing some initial HTML to get us started, as well as stylesheets and images folders.

In our HTML we can see references to both the CSS files. I've separated them so that the site's general styling can be used and moved the parts we'll be most interested in for this lesson to the file called header.css.

On prefixes

Just to note, I'll not be including prefixes in the code we write. This means adding -webkit- to properties such as transform and animation. Generally you'll want to put these in place before publishing your work but to keep things readible I won't be showing them during the coding. The downloaded files do include the needed prefixes.

HTML

<body>
  <header>
    <section class="header-content">
      <img src="images/rocky-dashed.svg" class="rocky-dashed">
      <h1 class="header-title">Your awesome landing page</h1>
      <h3 class="header-subtitle">A useful start for your projects</h3>
      <p class="header-button"><a href="#calls-to-action" class="button">Get started today</a></p>
    </section>
  </header>
</body>

This is the main content of our HTML. It's found in index.html. It's fairly simple for now, just a header element containing some content, images, text and headings. We'll be styling and animating this using our CSS.

CSS

Opening our CSS file in stylesheets/header.css we can see the initial styles for the landing page. We have the header, which is set to cover the full height of the viewport. To do this I've used the vh unit. This is expressed as a percentage, so 100vh would mean the entire viewport height.

header {
  align-items: center;
  background: #333;
  display: flex;
  font-size: 18px;
  height: 100vh;
  justify-content: center;
  overflow: hidden;
  perspective: 100px;
  position: relative;
  text-align: center;
  transform-style: preserve-3d;
}

header:after {
  background: #F9FCFF;
  content: "";
  height: 40rem;
  left: -5%;
  position: absolute;
    right: -5%;
    top: 90%;
  transform-origin: 0 0;
  transform: rotateZ(-4deg);
}

.header-title, .header-subtitle {
  color: #fff;
}

.header-subtitle {
  margin-bottom: 5rem;
  text-transform: uppercase;
}

.header-button {
  transform: translateZ(.1px);
  position: relative;
  z-index: 10;
}

Further down the file you'll see the content styling. I've followed a naming convention here where everything starts with header so that we can more easily find the source of this styling should we need to change things later.

Lastly you may have noticed the :after pseudoelement. A pseudoelement is something we can use to add additional content within CSS, for presentational purposes. In this case I'm adding a slanted white box to the bottom of the page to give it a non-square shape and make the overall design more interesting.

To allow for this, I've also set a couple of properties on the header to tell the browser to handle the positioning in 3D. I've set perspective and the transform-style property. This is so that I can push the "header-button" element forward to sit in front of the slanted white box. On small screens, the button would sit behind the box.

Before - how it looks

See the Pen Basic starting HTML for hero header.

Here's what we have to begin. It's a simple layout - hopefully simple enough that it can be tailored for your needs. Let's start with the boring background. We'll get rid of the plain background colour and animate in an image instead.

Let's add a background image

A great resource I often turn to for high quality images is Unsplash. Set up a few years ago as a reaction to the lack of good, free online photography resources, the site now offers a massive range of useful images that you can use in your designs for free. I chose this image for our example header. To prepare it for our use I scaled it down a bit, compressed it with TinyPNG and saved it in the images folder as background.jpg.

We can now add this to our CSS. Opening the header.css file in stylesheets, you'll see there's a header block at the top. In this we've set the background colour to a dark grey. We can remove that.

header {
  align-items: center;

  display: flex;
  font-size: 18px;
  ... etc
}

Now, rather than simply replacing what we removed with a new background property referencing our newly downloaded image, I'm going to do something more interesting.

Animating the background position

We could set a background-position on our background and then animate that, but I don't think that'll be a good idea.

There are two properties that animate very well in the browser. These are opacity and transform. We use the transform for positioning, scaling and rotating elements. Which is great, because browsers can animate these movements smoothly. However this means that if we were to try to animate the background-position property of our new background image, we might see "jankyness", where the animation isn't smooth.

So let's find a way to use transform to animate our background image more smoothly.

Adding a pseudo-element

Since the transform property needs to apply to individual HTML elements, we need to either add a "container" element to our page for the background image, or we can skip that and use a pseudo-element.

I love pseudo-elements. It's a way we can use CSS to create "virtual" elements on our page without adding more markup and can be quite versatile. They can be used for anything from adding extra text to an element, or in this case, adding extra layers of content.

If you look at the content in our header.css, you'll see we already have one pseudo-element, called header:after. This positions a white box at an angle at the bottom of the screen. Let's add another above that:

header:before {

}

Pseudo-elements need a content property, so we'll specify that. We'll also position this element so that it covers the entire header using absolute positioning. We then add the background:

header:before {
  background: linear-gradient(to bottom, rgba(0,0,0,0), rgba(0,0,0,.8)),
              url(../images/background.jpg) no-repeat bottom;
  background-size: cover;
  content: "";
  position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
  z-index: -1;
}

We're using multiple backgrounds here. By itself the image we've chosen is a bit bright, and text might be difficult to read on top of it. So I've added a linear gradient. Multiple backgrounds always confuse me but they start with the front-most background first, then the second background will appear behind that. If you have it the other way round you might not see your gradient as the background image will be in the way.

Lastly we're setting the background-size to "cover". This tells the browser to crop the image either horizontally or vertically to fill the entire shape. It will lose some of the image but won't stretch it. It's worth experimenting with different settings here, and trying to see if maybe positioning the image in the center or top or bottom looks best.

See the Pen Landing page with background image.

Now we have a background in place, inside it's own pseudo-element container. Let's animate the pseudo-element.

Fading slide down

We'd like this image to fade in, but also slide down. Combining animations like this can make for a nice result and it will allow us to try some timing functions to get a really smooth result.

To begin let's write our keyframes. We set out keyframes like so, with a name, fade-slide-down and in this case a starting and end position.

@keyframes fade-slide-down {
  0% {

  }
  100% {

  }
}

At "0%" we want to tell the browser the background's starting position. It needs to be invisible, and also positioned a little higher. We do this using the transform property with translate.

@keyframes fade-slide-down {
  0% {
    opacity: 0;
    transform: translateY(-4rem);
  }
  100% {
    opacity: 1;
    transform: none;
  }
}

At "100%" we have the ending position. It has an opacity of 1 so that we can see it, and there is no transform. This gives us both vertical movement and a change in opacity.

With the keyframes built, let's apply them to the pseudo-element. Inside the header:before block, add the following line.

animation: fade-slide-down .5s ease-out forwards;

This applies our animation to the pseudo-element. Going from left to right, it first tells it the name of the animation, then the duration of 1/2 a second, the timing function of ease-out and finally the animation-fill-mode.

The last one can be a little tricky but it basically means that the animation should stop on the last keyframe and stay there. In this way the styles in the "100%" keyframe are the styles that will remain once the animation has finished.

Let's see this in action. Press "Rerun" on the CodePen to see the animation again.

See the Pen Landing page with animated background image (.5s).

So the background fades and slides in. That's good. But the animation is a little bit sudden. Let's try to improve it.

First let's try adjusting the duration. UI animations tend to need to happen quite fast, and usually a value of 0.5 seconds is good as a rule of thumb. However for our landing page we'd like the animation to feel a little more luxurious, like it's not in a big hurry.

So let's set the duration to two seconds.

animation: fade-slide-down 2s ease-out forwards;

Here's how it looks.

See the Pen Landing page with animated background image (2s).

That's certainly more relaxed. But it still feels a little stilted. Next, let's look at the timing function.

Custom timing functions

Browsers give us a set of pre-built timing functions to work with. This include ease-in, ease-out and more. They're handy but they tend to be a little bit boring and not always ideal for larger animations. We can instead create our own timing function to change the way our pseudo-element moves on the screen.

What would be better would be if the animation starts a little quicker but then very gradually slows with more of an elongated glide toward the end. It would create a more subtle effect toward the end of the animation, ideally making it less likely to clash with any other animations going on after the background has appeared.

I'm a fan of Cubic-Bezier.com but you can use the built-in inspector tools in Chrome or Firefox to edit your cubic bezier curves.

In this case I'm going to create a kind of extreme variation of ease-in. Moving the starting point to "0,.5" and the ending point to "0,1" it'll start off really fast, but then take it's time toward the end.

A bezier curve with points 0,.5,0,1

Let's see how that looks in the animation property.

animation: fade-slide-down 2s cubic-bezier(0, 0.5, 0, 1) forwards;

We've taken the 4 numbers output by the website and wrapped it in cubic-bezier() inside our animation property. We can now see it in action.

See the Pen Landing page with animated background image (2s + bezier).

The background now seems to fly in but then slow down gracefully, tapering off to an almost imperceptible finish. That's better.

Not so fast

Before we call this one done, let's think about the timing of when the animation starts. It's a little bit sudden. The page loads and the animation starts immediately. The effect is a little jarring. Let's add some delay. We update our CSS to include a delay of ".5s".

animation: fade-slide-down 2s .5s cubic-bezier(0, 0.5, 0, 1) forwards;

See the Pen Landing page with animated background image (2s + bezier + delay).

Ouch! That's not great. The background is showing, then it disappears when the animation starts. Let's fix that by adding in a starting opacity for the pseudo-element. Our finished CSS for the pseudo-element should now look like this.

header:before {
  animation: fade-slide-down 2s .5s cubic-bezier(0, 0.5, 0, 1) forwards;
  background: linear-gradient(to bottom, rgba(0,0,0,0), rgba(0,0,0,.8)),
              url(../images/background.jpg) no-repeat bottom;
  background-size: cover;
  content: "";
  opacity: 0;
  position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
  z-index: -1;
}

See the Pen Landing page with animated background image (2s + bezier + delay + opacity).

That's better. The background starts empty, then a moment later fades in while sliding down.

Experiment and find your own style

With timing functions it's always worth experimenting and finding what works for you and the project you're working on. Never stick to the pre-built timing functions!

We'll be using lots of different timing functions as we go through creating the animations on this landing page. It's always worthwhile trying your own and seeing what works for you.

Next: Introducing the titles

In this lesson we've learned about multiple CSS backgrounds as well as how to apply backgrounds and animations to pseudo-elements. We've also seen how tweaking the timing functions on the animation can make a boring animation more interesting. We've also seen how we can apply an animation delay to add better pacing to the way content is revealed.

Next we'll apply some of these ideas of timing and pacing to the content in front of the background image. We'll create and animate the titles and logo.