DEV Community

Sid
Sid

Posted on

Pirates and CSS animations

I wrote this post for my newsletter, sign up here to get emails like these every week.

Hi!

For my course on React Hooks, I have to make a bunch of animations for it to be interesting.

final result

A few folks asked how do those animations work...

tweets

You'll be surprised at how easy it is with CSS.

First, let's break down our composition:

  1. We have a bird and a pirate that's hanging from the bird
  2. They enter together from the right side of the screen
  3. After reaching half way, the bird drops the pirate and leaves the screen from the left
  4. The pirate falls to the bottom of the screen

 

Step #1: Characters

characters

<div class="bird"></div>
<div class="pirate"></div>
Enter fullscreen mode Exit fullscreen mode
.bird {
  background-image: url('./bird.png');
}
.pirate {
  background-image: url('./pirate-hanging.png');
}
Enter fullscreen mode Exit fullscreen mode

With some hit and trial, we can position the pirate at exactly the right spot.

hanging

.pirate {
  top: 27px;
  margin-left: 49px;
}
Enter fullscreen mode Exit fullscreen mode

We need another image for the pirate in his falling state. Because the image is a vector image (SVG), we can modify it to get the right posture. (I used sketch, but you do this in any image editor)

redesign

Okay, now that we have the graphics ready, let's think about the choreography.

 

Step #2: Choreography

Before we write any code, it's useful to write down the different steps of the animation.

t = 0     1     2     3     4     5         6     7     8     9
    ---------  enter  --------    pause   ----- exit ----

1: 0s - 5s : bird: fly in + pirate: fly in
2: 5s - 6s : pause
3: 6s - 9s : bird: fly out + pirate: fall
Enter fullscreen mode Exit fullscreen mode

Let's start with the bird flying in.

We want the bird to come in from outside the screen to the middle. We can attach the same animation to the pirate because he is hanging to the bird for this part.

Shout out to my friend Andres who helped me improve this article. He pointed out that animating position produces a repaint on every keyframe and using translation is way more performant. You should follow Andres on twitter, he's a CSS wizard.

enter

.bird {
  animation: bird-enter 5s ease-out;
}
.pirate {
  animation: bird-enter 5s ease-out;
}

@keyframes bird-enter {
  from {
    /* right end of the screen */
    transform: translateX(100vw);
  }
  to {
    /* half way */
    transform: translateX(50vw);
  }
}
Enter fullscreen mode Exit fullscreen mode

Looking good!

By default, the characters will reposition snap to their default position after the animation is finished. That's because CSS animations do not affect the element before the first keyframe is played or after the last keyframe is played.

We can use the property animation-fill-mode to override this behavior:

.bird .pirate {
  animation-fill-mode: forwards;
}
Enter fullscreen mode Exit fullscreen mode

forwards will make the element stay in the same position as the final keyframe.

 

Alright, let's talk about the exit.

An element can have multiple animations applied to it.

.bird {
  /*         ↓ enter animation       ↓ exit animation   */
  animation: bird-enter 5s ease-out, bird-exit 3s ease-in;
}
Enter fullscreen mode Exit fullscreen mode

Both of these animations will start at the same time though. That's not good, we want the second animation (exit) to start some time after the first animation finishes (enter).

This is where the animation-delay property comes in. The delay for the exit animation should be the duration of the first animation + the pause we want to take between animations: 5 + 1 = 6s.

Because we have multiple animations, we need to specify the delay for each of the animation.

midway

.bird {
  /*         ↓ enter animation       ↓ exit animation   */
  animation: bird-enter 5s ease-out, bird-exit 3s ease-in;
  animation-delay: 0s, 6s;
  /* 0s delay for the entry animation, 6s for the exit  */
}

@keyframes bird-exit {
  from {
    transform: translateX(50vw);
  }
  to {
    /* exit outside the frame */
    transform: translateX(-10vw);
  }
}
Enter fullscreen mode Exit fullscreen mode
Sidenote: you can also write the above code with the animation shorthand, where the 4th argument is delay: bird-exit 3s ease-in 6s.

Okay, now let's talk about the pirate's exit. The timing is the same as the bird, but the animation is a little different. We want the pirate to fall down from it's original position to the bottom end of the screen.

pirate falling

.pirate {
  /*         ↓ enter animation       ↓ exit animation   */
  animation: bird-enter 5s ease-out, fall 3s ease-in;
  animation-delay: 0s, 6s;
  /* 0s delay for the entry animation, 6s for the exit  */
}

@keyframes fall {
  from {
    /* keep it's X position */
    transform: translateX(50vw);
  }
  to {
    /* keep X position, fall to the bottom end of the screen */
    transform: translateX(50vw) translateY(100vw);
  }
}
Enter fullscreen mode Exit fullscreen mode

Okay that's a good start, but we also need to change the image.

We can attach a different class to the pirate element with javascript to have a different background image. But, we can also achieve this through css.

If we supply the new background-image to the element in the from and to of the animation, it will apply it for the whole duration.

To make the effect even better, let's rotate the pirate while he falls.

@keyframes fall {
  from {
    /* keep it's X position */
    transform: translateX(50vw);
    background-image: url('./pirate-falling.png') rotate(0deg);
  }
  to {
    /* keep X position, fall to the bottom end of the screen */
    transform: translateX(50vw) translateY(100vw) rotate(90deg);
    background-image: url('./pirate-falling.png');
  }
}
Enter fullscreen mode Exit fullscreen mode

This is the combined result:

pirate falling

If we use the SVG version of the pirate instead of PNG, we can go really deep and animate each part of the pirate. Something like:

.pirate .hand {
  animation: raise-up 2s ease-in;
}
Enter fullscreen mode Exit fullscreen mode

We don't have to, of course. A few lines of CSS keyframes combined together gave a pretty good result too!

If you want to experiment, here's a codepen link to this animation.

Hope that was useful on your journey

Sid


newsletter

Top comments (8)

Collapse
 
brandonmcconnell profile image
Brandon McConnell • Edited

Great introductory lesson to CSS animations! Normally I'd throw all of these animation steps into one unified animation per element, but it reads really well and it's much more user friendly the way you did it.

Here's a CSS animation I made recently which creates the effect of auto-typing text using only CSS (SCSS). Still buttoning up a few quirks, but it works for the most part. Similar to the typed.js effect.

cdpn.io/e/pGKaRK

*does not work in Safari or mobile browsers (at least for iPhone)

Collapse
 
siddharthkp profile image
Sid

Whaaaaaat, this is amazing Brandon!

Collapse
 
torpne profile image
Kevin McKenna

Fantastic article. Definitely something I'll play with when I have some free time

Collapse
 
rhymes profile image
rhymes

Wow, that's impressive! Thank you Sid!

Collapse
 
praneetnadkar profile image
Praneet Nadkar

Brilliant! You made it look easy!

Collapse
 
francocorreasosa profile image
Franco Correa

CSS Animations can be mindblowing powerful! amazing article, as always 👏 👏

Collapse
 
siddharthkp profile image
Sid

Fraaaanco! hi

Collapse
 
thomas_ph35 profile image
Thomas

css animation are so underrated !