Dang…is it Novem­ber already?! Time is a fun­ny thing when you’re hav­ing this much fun. 

Wel­come back grasshop­per, what a crazy year these last 12 months have been. I hope you have made it here unscathed. We will need your good vibes for what lies ahead. Brace your­self for one more ride down the GSAP roller-coast­er, this post is going to be rad!

Let’s begin

In the first part of this beau­ti­ful duet, I showed you how to cre­ate a sim­ple loader using GSAP; we turned a cir­cle into a square and vice-versa.

For this sec­ond part, we’re tak­ing our inspi­ra­tion direct­ly from the 90s. Specif­i­cal­ly, from a clas­sic and rev­o­lu­tion­ary movie you Amer­i­cans know as Space Jam.

Spacejam image

Now go find your old Air Jor­dans because you’re going to learn how to cre­ate a boun­cy basketball!

Struc­tur­ing the HTML

The ani­ma­tion we’re work­ing with is made of two ele­ments: one for the ball and one for the shadow.

<div class="ball"></div>
<div class="shadow"></div>

Adding the CSS

Before we start, I like to reset all the prop­er­ties and cen­ter the ele­ments in the screen using flexbox:

* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}

body {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  height: 100vh;
  background-color: #ffc30f;
}

OK, now we can style this beauty:

.ball {
  width: 60px;
  height: 60px;
  border-radius: 50%;
  background-color: #c70039;
  background: linear-gradient(to bottom, rgba(199,0,56,1) 0%, rgba(144,12,63,1) 100%);
  transform: translateY(-100px);
}

.shadow {
  width: 60px;
  height: 30px;
  background: radial-gradient(ellipse at center, rgba(88,24,69,1) 0%, rgba(237,237,237,0) 50%);
  position: relative;
  z-index: -1;
  transform: translateY(75px);
  opacity: .1;
}

Notice that:

  1. We added position: relative and z-index: 1. This way we ensure that the shad­ow will always remain behind the ball.
  2. We moved the ball a frac­tion upward and the shad­ow a frac­tion down­wards. This will be the ini­tial state of the animation.

Ani­ma­tion states

Let’s review what we want to do. I cre­at­ed this advanced chart to demonstrate:

Steps bouncy ball image
  1. In the first state, the ball starts at its high­est posi­tion and we dim the shad­ow, like some sort of invis­i­ble floor.
  2. In the sec­ond state, the ball reach­es its low­est posi­tion, i.e., the floor.”
  3. In the final state, we smash the ball slight­ly to sim­u­late its impact on the sur­face. We also increase the size and strength of the shad­ow to sim­u­late its proximity.

Under­stand­ing the timeline

Back to the future image

As I men­tioned in the pre­vi­ous arti­cle, GSAP is made of 4 libraries that allow us to cre­ate iso­lat­ed or chained animations.

For the iso­lat­ed ani­ma­tions, we cre­ate tweens using the Tween­Lite or Tween­Max libraries.

For the chained ani­ma­tions, we use the Time­lineLite or Time­line­Max libraries. These help us cre­ate a tween sequence in chrono­log­i­cal order.

The main advan­tage of a Time­line is that the ani­ma­tion can be con­trolled with meth­ods like play(), stop(), restart() and reverse(), giv­ing us total con­trol over the tim­ing of events.

In our ani­ma­tion, we’ll use the Time­line­Max library. It has all the Time­lineLite func­tion­al­i­ty, plus oth­er impor­tant prop­er­ties like yoyo and repeat that we’ll use ahead.

The ball animation

Let’s start by select­ing the elements:

var ball = document.querySelector('.ball');
var shadow = document.querySelector('.shadow');

Then we ini­ti­ate the Timeline:

var tl = new TimelineMax();

And add the first ani­ma­tion, mov­ing the ball for a half sec­ond down­wards with the method to():

var tl = new TimelineMax();

tl.to(ball, .5, {
    y: -100
  });

Next, we want to make the ball reach the floor” quickly.

In order to do that in a frac­tion of sec­ond we’ll reduce the bal­l’s height and change its inte­ri­or bor­der-round­ness slight­ly cre­at­ing that impact effect:

var tl = new TimelineMax();

tl.to(ball, .5, {
   y: 100
  })
  .to(ball, .10, {
    scaleY: 0.6,
    transformOrigin: 'center bottom',
    borderBottomLeftRadius: '40%',
    borderBottomRightRadius: '40%'
  });

Note: The prop­er­ty transformOrigin: "center bottom" is added to make the impact ani­ma­tion more real­is­tic. By default, the scale ani­ma­tion always occurs towards the cen­ter. By chang­ing it to cen­ter bot­tom” the ani­ma­tion occurs towards the cen­ter, over the X axis and towards the bot­tom over the Y axis.

Shad­ow animation

We’ve already done all the ani­ma­tions relat­ed to the ball, now let’s start the shad­ow animation.

We want the shad­ow to get big­ger and stronger as the ball gets clos­er to the imag­i­nary floor. Let’s do this by adding one more to() method into the timeline.

var tl = new TimelineMax();

tl.to(ball, .50, {
    y: 100
  })
  .to(ball, .10, {
    scaleY: 0.6,
    transformOrigin: 'center bottom',
    borderBottomLeftRadius: '40%',
    borderBottomRightRadius: '40%'
  })
  .to(shadow, .5, {
    width: 90,
    opacity: .7
  });

In the exam­ple above, the ani­ma­tions occur after the ball ani­ma­tion. As the ball over­laps the shad­ow, we can’t see it.

We want the shad­ow ani­ma­tion to occur at the same time. One way of doing that is using labels.

Labels

A label is a point in the time­line that we can use as a reference.

To make it clear­er, let’s add a label named start” in the begin­ning of the Time­line and refer to it in the shad­ow animation.

var tl = new TimelineMax();

tl.add('start')
  .to(ball, .50, {
    y: 100
  })
  .to(ball, .10, {
    scaleY: 0.6,
    transformOrigin: 'center bottom',
    borderBottomLeftRadius: '40%',
    borderBottomRightRadius: '40%'
  })
  .to(shadow, .50, {
    width: 90,
    opacity: .7
  }, 'start');

What we’re doing is telling the shad­ow ani­ma­tion to start at the point marked in the time­line as start”.

This way, the shad­ow ani­ma­tion is as long as the ball ani­ma­tion. Both begin and end at the same time.

Ball timeline image

Repeat­ing it infinitely

We have every­thing we need to make the ball bounce, now all we need to do is refine it.

First, we make the ani­ma­tion repeat infi­nite­ly. To do that we pass the prop­er­ty repeat: -1 to the Time­line instance.

var tl = new TimelineMax({ repeat: -1 });

How­ev­er, this alone isn’t enough. The ani­ma­tion does­n’t work on a con­tin­u­ous flow. Instead, it’s always restart­ing from its ini­tial state, doing a kind of jump.”

To make the ani­ma­tion return to its ini­tial state with­out the jump,” we also pass the prop­er­ty yoyo: true to the Time­line instance:

var tl = new TimelineMax({ repeat: -1, yoyo: true });

Eas­ing

The last step is adding flu­id­i­ty to the animation.

That means apply­ing an eas­ing func­tion. This way the ani­ma­tion does­n’t seem hard” and unnatural.

GSAP pro­vides a lot of pre-defined ease func­tions, you can see them here.

In this tuto­r­i­al, we want the ball to look like it’s being affect­ed by the grav­i­ty, i.e., it speeds up when going down and slows down when going up.

To add that effect, let’s use an ease func­tion called Circ.easeIn.

Here we add that func­tion to the ball and shad­ow animations:

var tl = new TimelineMax({ repeat: -1, yoyo: true });

tl.add('start')
  .to(ball, .50, {
    y: 100,
    ease: Circ.easeIn
  })
  .to(ball, .10, {
    scaleY: 0.6,
    transformOrigin: 'center bottom',
    borderBottomLeftRadius: '40%',
    borderBottomRightRadius: '40%'
  })
  .to(shadow, .50, {
    width: 90,
    opacity: .7,
    ease: Circ.easeIn
  }, 'start');

And voilà!

In case it looks too much like the ball is stick­ing on the ground before bounc­ing, we can advance the smash” animation.

To cre­ate a bet­ter effect, we can add a val­ue after its ani­ma­tion prop­er­ties. For this exam­ple, the val­ue will be "-=0.075". This way, we indi­cate that the ani­ma­tion prop­er­ties must be applied ear­li­er in the time­line, i.e., while the pre­vi­ous ani­ma­tion is still running!

Now the final result feels smooth and natural.

The loader is final­ly done.

var ball = document.querySelector('.ball');
var shadow = document.querySelector('.shadow');

var tl = new TimelineMax({ repeat: -1, yoyo: true });
tl.add('start')
  .to(ball, .50, {
    y: 100,
    ease: Circ.easeIn
  })
  .to(ball, .10, {
    scaleY: 0.6,
    transformOrigin: 'center bottom',
    borderBottomLeftRadius: '40%',
    borderBottomRightRadius: '40%'
  }, '-=0.075')
  .to(shadow, .50, {
    width: 90,
    opacity: .7,
    ease: Circ.easeIn
  }, 'start');

Con­clu­sion

There are infi­nite ways to cre­ate a boun­cy ball, some sim­pler, oth­ers more com­plex. This post has the hum­ble inten­tion of show­ing you how a GSAP time­line works in a prac­ti­cal way.

For more exam­ples of Time­line ani­ma­tions, check out this link.

In the last episode of this tril­o­gy, we’ll explore how to cre­ate stag­gered animations.

I look for­ward to our next encounter. Stay safe out there friends!

If you made it this far, you may also like

A Journey into Animated Loaders with GSAP - Part 1

Feral stylin with thiago

Among the many great tutorials on this subject, this walkthrough promises to be just about average.