JavaScript
Article
By George Martsoukos

Animating Bootstrap Carousels with the GSAP Animation Library

By George Martsoukos

In a previous post, I covered the process of converting a Bootstrap carousel into a full screen carousel with a random initial image. Today, I’ll build on that and show you how to use GSAP (GreenSock Animation Platform), a popular JavaScript library, to add animations to your Bootstrap carousels.

A carousel horse wearing the GSAP cape

Before going any further, let’s look at what we’ll be building. There’s also a demo at the end of the article.

Note: We’ll use Bootstrap 3 styles to create the carousel. However, there’s also a second version of it available which is compatible with Bootstrap 4 alpha release (latest release at the time of this writing).

Building the Carousel

Be sure to include Bootstrap and jQuery (Bootstrap’s JavaScript components require it) in your page, for example, from a CDN:

<!DOCTYPE HTML>
<html>
  <head>
    <meta charset="utf-8">
    <title>Using GSAP to Animate Bootstrap 3 Carousels</title>
    <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
  </head>
  <body>
    ...
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
  </body>
</html>

Note that Bootstrap’s JavaScript requires jQuery version 1.9.1 or higher, but lower than version 3.

The basic structure of our carousel looks like this:

<div id="mycarousel" class="carousel slide" data-ride="carousel">
  <ol class="carousel-indicators">
    <li data-target="#mycarousel" data-slide-to="0" class="active"></li>
    <li data-target="#mycarousel" data-slide-to="1"></li>
  </ol>

  <div class="carousel-inner" role="listbox">

    <!-- first slide -->
    <div class="item first active" id="1">
      <!-- content here -->
    </div>

    <!-- second slide -->
    <div class="item second" id="2">
      <!-- content here -->
    </div>

  </div><!-- /carousel-inner -->

</div>

As you can see, it contains two slides. The first slide has a class of first and an id of 1, while the second one has a class of second and an id of 2.

Regarding their styles:

  • We set their height equal to the viewport height and
  • give them different background colors.

The associated CSS rules:

.item {
  height: 100vh;
}

.first {
  background: #D98F4F; /*orange*/
}

.second {
  background: #2c9cae; /*lightblue*/
}

This should be enough to give us a working carousel.

Building the First Slide

Next, we take advantage of Bootstrap’s helper classes (e.g. grid classes) to set up the contents for our slides.

The markup for the first slide is the following:

<div class="item first active" id="1">
  <div class="carousel-caption">
    <div class="container">
      <div class="row">

        <div class="col col-xs-6">
          <h2 class="title">
            <!-- content here -->
          </h2>
          <p class="desc">
            <!-- content here -->
          </p>
          <ul class="list">
            <!-- list items here -->
          </ul>
        </div>

        <div class="col col-xs-6">
          <div class="pc-wrapper">
            <img class="pc" src="IMG_PATH" alt="" width="" height="">
            <div class="price">
              <!-- content here -->
            </div><!-- /price -->
          </div><!-- /pc-wrapper -->
          <img class="keyboard" src="IMG_PATH" alt="" width="" height="">
          <button type="button" class="btn btn-danger btn-lg">
            <!-- content here -->
          </button>
        </div>

      </div><!-- /row -->
    </div><!-- /container -->
  </div><!-- /carousel-caption -->
</div><!-- /item -->

If you’re following along, be sure to replace IMG_PATH with something sensible. You can grab the URLs for the images from the final demo.

Here’s its visualization:

Animating Bootstrap Carousels: The first slide of the carousel

Building the Second Slide

In the same way, here’s the markup for the second slide:

<div class="item second" id="2">
  <div class="carousel-caption">
    <div class="container">

      <h2 class="title">
        <span>
          <!-- content here -->
        </span>
      </h2>

      <div class="row">

        <div class="col-xs-4 cms-wrapper">
          <div class="cms">
            <div class="front">
              <!-- content here -->
            </div>
            <div class="back">
              <img class="img-responsive" src="IMG_PATH" alt="">
            </div><!-- /back -->
          </div><!-- /cms -->
          <p class="info">
            <!-- content here -->
          </p>
        </div><!-- /cms-wrapper -->

        <!-- two more columns here -->

      </div><!-- /row -->

      <div class="source">
        <!-- content here -->
      </div><!-- /source -->

    </div><!-- /container -->
  </div><!-- /carousel-caption -->
</div><!-- /item -->

And its visualization:

Animating Bootstrap Carousels: The second slide of the carousel

Note: For simplicity, we won’t extensively cover the styles for the inner parts of our slides. We’ll only refer to the styles that are important for the animations.

Initializing the Carousel

Next we initialize the carousel and disable default autoplay by passing interval:false to the configuration object:

var $carousel = $("#mycarousel");

$carousel.carousel({
  interval: false
});

Adding Keyboard Navigation

By default, Bootstrap carousel isn’t compliant with accessibility standards. In our case though, let’s make the carousel a bit more accessible by adding keyboard navigation.

Here’s the required code:

$(document).keyup(function(e) {
  // right arrow
  if(e.which === 39) {
    $carousel.carousel("next");
  // left arrow
  } else if(e.which === 37) {
    $carousel.carousel("prev");
  }
});

So far, we’ve built a basic carousel which supports keyboard navigation. You can see it live in the following Codepen demo: A Simple Bootstrap 3 Carousel

--ADVERTISEMENT--

Animating Bootstrap Carousels – First Animations

At this point, let’s try to make the carousel more appealing by adding some animations. To achieve this, we’ll take advantage of GSAP, one of the most powerful JavaScript animation libraries out there.

Getting Started With GSAP

To incorporate GSAP into our projects, we have to visit its site and from there click the download button which appears in the top right corner of the page. This will open a modal dialog with a link to the project on a CDN.

How to download GSAP

If we then select the Customize radio button, we can select the parts of the library we want to use. For our project however, we’ll keep things simple and include just the full robust version of it.

How to include GSAP into our Codepen demo

Remember we had to add jQuery to our project because Bootstrap carousel depends on it. But, keep in mind that GSAP is a pure JavaScript library, and thus it doesn’t require it.

Animating the First Slide

By default, we want the content of our slides to be hidden:

.carousel-caption {
  opacity: 0;
}

Only when the page loads do we reveal and animate the contents of the first slide. To do this, we take advantage of TimelineLite, an animation tool that helps us build a sequence of tweens.

So, as soon as all page assets are ready, the firstTimeline function is executed:

// this variable stores the first timeline
var firstTl;

$(window).on("load", function() {
  firstTl = firstTimeline();
});

This function returns a timeline which determines the animations for the first slide:

function firstTimeline() {
  var tl = new TimelineLite();

  tl
    .to(".first .carousel-caption", 0.1, {opacity: 1})
    .from(".first .pc", 1, { y: -300, opacity: 0, ease: Bounce.easeOut})
    .from(".first .keyboard", 1, { y: 300, opacity: 0, ease: Bounce.easeOut}, "-=1")
    .staggerFrom(".first .list li", 0.75, {opacity: 0, cycle: {x: [-200, 200]}, ease: Power2.easeOut}, 0.15, "-=0.5")
    .from(".first .desc", 0.7, {x: 500, opacity: 0, ease: Power4.easeOut}, "-=0.5")
    .from(".first .title", 0.7, {x: -500, opacity: 0, ease: Power2.easeOut}, "-=0.7")
    .from(".first .price", 0.7, {scale: 0.01, ease: Power4.easeOut})
    .from(".first button", 0.7, { y: -700, autoAlpha: 0, ease: Bounce.easeOut}, "-=0.3");

  return tl;
}

More specifically, inside the function above we do the following:

  1. Create a TimelineLite.
  2. Add tweens to the timeline using its to, from, and staggerFrom methods.
  3. Return the timeline.

Let’s take note of the parameters that we pass to the to and from methods:

  1. The DOM element that we want to animate.
  2. The duration of the animation in seconds.
  3. An object containing the properties that should be tweened and their respective (starting or end) values. Moreover, this object can also have some other special properties like the ease property which defines the easing function.
  4. The placement of the tween in the timeline. In other words, when this tween should be executed. For example, we want to run the animations for the .first .pc and .first .keyboard elements at the same time. To do this, we set the value of the position parameter of the .first .keyboard element to "-=1". The number “1” inside this value matches the duration of the animation of the .first .pc element.

Similar to the to and from methods, we pass the same parameters to the staggerFrom method. The only difference is that we define one additional parameter (the fourth one) which specifies each tween’s start time. In our example, this value is set to 0.15. That said, the target elements won’t appear simultaneously but there will be a very small gap between their animations. Change this value to something big (e.g. 5), to see a clear difference in the effect.

Obviously, the best way to understand how the animations above work is to read the documentation. Plus, use the developer tools of your browser to see what styles GSAP applies to the target elements.

So let’s briefly have a closer look at the animations assigned to the button.

Initially the button has autoAlpha: 0 and y: -700. That means it’s visually hidden (opacity: 0, visibility: hidden) and located 700 pixels up from its original position (transform: matrix(1, 0, 0, 1, 0, -700)).

Animating Bootstrap Carousels: Which styles initially GSAP applies to the button before animations

Then, when the animation plays, it becomes visible (opacity: 1, visibility: inherit) and returns back to its default position (transform: matrix(1, 0, 0, 1, 0, 0)).

Animating Bootstrap Carousels: Which styles finally GSAP applies to the button after animations

Finally, the animation starts 0.3 seconds before the end of the timeline.

Tip: Use TimelineLite’s duration method to retrieve its duration. The key here is to understand how this value is calculated.

Great job so far! The animations for the first slide are ready! You can see them live in this Codepen demo: Animating Bootstrap Carousels with the GSAP (Part 1).

Using Bootstrap’s Carousel Events

As we leave the first slide and move on to the second one (and vice versa), the contents of our slides should be hidden. It should appear as soon as the carousel completes its slide transition. To accomplish this behavior, we take advantage of the following things:

  1. The slide.bs.carousel and slid.bs.carousel events that Bootstrap provides
  2. and GSAP’s TweenLite animation tool that helps us build a single tween. Note that in the previous section we used a TimelineLite to create a sequence of tweens.

Here’s the necessary code:

var $carouselCaption = $(".carousel-caption");

$carousel.on("slide.bs.carousel", function() {
  TweenLite.to($carouselCaption, 0.1, {opacity: 0});
});

$carousel.on("slid.bs.carousel", function () {
  TweenLite.to($carouselCaption, 0.1, {opacity: 1});
});

As we’ve discussed earlier, when the page loads, the animations for the first slide run. But, when should they run again? Plus, what about the second slide and its animations? To answer all these questions, let’s add a few lines of code to the callback of the slid.bs.carousel event:

// these variables store the timelines
var firstTl, secondTl;

$carousel.on("slid.bs.carousel", function (e) {
  TweenLite.to($carouselCaption, 0.1, {opacity: 1});
  var slideId = e.relatedTarget.id;

  if(slideId === "1") {
    firstTl.restart();
  } else if(slideId === "2") {
    secondTl = secondTimeline();
  }
});

The code above does the following:

  1. Checks the id of our slides.
  2. If the id is equal to 1, the first slide has been loaded, and thus we’ve to replay the animations for this slide. Remember we’ve already created a timeline for these animations (within the firstTimeline function), so we can use its restart method to play it again.
  3. If the id is equal to 2, the second slide has been loaded, and thus the secondTimeline function is executed (see next section).

Animating the Second Slide

The secondTimeline function returns a timeline which determines the animations for the second slide:

function secondTimeline() {
  var tl = new TimelineLite({onComplete: allDone});

  tl
    .from(".second .title", 0.5, { y: -400, opacity: 0, ease: Sine.easeInOut})
    .staggerFrom(".second .cms-wrapper", 0.5, {scale: 0, rotation: 180, ease: Power2.easeInOut, onComplete: completeFunc}, 1.2);

  return tl;
}

More specifically, inside the function above we do the following:

  1. Create a TimelineLite.
  2. Add tweens to the timeline using its from and staggerFrom methods.
  3. Return the timeline.

Although this timeline looks like the one we’ve previously created for the first slide, there are some things here that we haven’t seen before.

First, look at the object that we pass to the staggerFrom method. This contains a special property called onComplete. This property holds a function (i.e. completeFunc()) that’s fired when the animations for each of the target elements finish.

Let me explain.

By default, only the elements with the class of .second .front (i.e. numbers) are visible.

How the cms-wrapper element of the second slide initially looks like before animations

And the .second .back (i.e. CMS logos) and .second .info (i.e. CMS names) elements are hidden.

Here are the corresponding CSS rules:

.second .back {
  display: none;
  transform: scale(0);
}

.second .info {
  opacity: 0;
  transform: translateY(40px);
}

When the completeFunc function runs for each of the .second .cms-wrapper elements, we set up tweens that affect the visibility of the child elements. Specifically, we hide the .second .front element and then show (after 0.3 seconds delay) the .second .back and .second .info elements.

What the cms-wrapper element of the second slide looks like after animations

Below you can see the related JS code:

function completeFunc() {
  var $this = $(this.target),
      $info = $this.find(".info"),
      $front = $this.find(".front"),
      $back = $this.find(".back");

  TweenLite.to($front, 0.3, {display: "none", scale: 0});
  TweenLite.to($back, 0.3, {display: "block", scale: 1, delay: 0.3});
  TweenLite.to($info, 0.3, {opacity: 1, y: 0, delay: 0.3});
}

The second new thing in this timeline is the callback that runs when all animations finish. We specify this by passing a configuration object with an onComplete property (whose value is the allDone function) to the constructor function.

When the timeline completes, this function is triggered and shows the .second .source element which was initially invisible (opacity: 0, visibility: hidden).

Animating Bootstrap Carousels: How the source element of the second slide looks like after animations

Here’s the allDone function:

function allDone() {
  TweenLite.to(".second .source", 0.3, {autoAlpha: 1, delay: 1});
}

At this point, let’s examine the current state of our carousel: Animating Bootstrap Carousels with the GSAP (Part 2).

The animations look good but there’s still a small problem with regards to the second slide. To be more specific, the first time we visit the second slide, the animations work as expected. However, in any other case (test it), the result isn’t the desired one, as the CMS logos don’t animate in.

To fix this issue, we have to restart the corresponding timeline (remember we did the same for the first slide) and clear the tweens (inline styles) defined in the completeFunc and allDone functions. Lastly, we pause the second timeline when the first one is active. With that in mind, once again we update the callback of the slid.bs.carousel event like this:

var counter = 0,
    firstTl,
    secondTl;

$carousel.on("slid.bs.carousel", function (e) {
  TweenLite.to($carouselCaption, 0.1, {opacity: 1});
  var slideId = e.relatedTarget.id;

  if(slideId === "1") {
    firstTl.restart();
    secondTl.pause();
  } else if(slideId === "2") {
    if(counter === 0) {
      secondTl = secondTimeline();
    } else {
      TweenLite.set([".second .front", ".second .back", ".second .info", ".second .source"], {clearProps:"all"});
      secondTl.restart();
    }
    counter++;
  }
 });

So finally, here’s the new version of our carousel: Animating Bootstrap Carousels with the GSAP (Part 3).

Customizing the Carousel When JavaScript is Disabled

The carousel is almost ready. Before wrapping things up, let’s customize its appearance when JavaScript is disabled. In such a scenario, the slides have to appear below each other and a custom message should encourage users to enable JavaScript.

Here’s the desired visualization:

What the carousel looks like with JavaScript disabled

One way to accomplish this behavior is by using the <noscript> tag. This option is great, yet it adds some additional code to our HTML, so let’s try something a little bit different.

By default, we add the no-js class to the <html> tag. As the browser parses the page, if JavaScript is enabled in this browser, the class is removed.

So first, in the HTML we add this code:

<p class="msg">
  It seems you have JavaScript disabled. The carousel doesn't work well without JavaScript. Please enable it!
</p>

Then, in the CSS this one (mostly reset rules):

.msg {
  display: none;
  position: fixed;
  top: 5px;
  left: 50%;
  transform: translateX(-50%);
  padding: 7px;
  border: 5px solid #fff000;
  text-align: center;
  color: #fff;
  background: rgba(0, 0, 0, .85);
  z-index: 999;
}

.no-js .carousel-inner > .item {
  display: block;
}

.no-js .carousel-caption,
.no-js .second .info,
.no-js .second .source {
  opacity: 1;
}

.no-js .second .info {
  transform: none;
 }

.no-js .second .source {
  visibility: visible;
}

.no-js .carousel-indicators {
  display: none;
}

.no-js .msg {
  display: block;
}

Finally, in the <head> tag of our page we insert the following code snippet:

<script>
  document.documentElement.className = "";
</script>

Removing the no-js class from the HTML element

Keep in mind that we place this code within the <head> tag because we want it to be executed before the browser starting to paint the elements. This is important in order to prevent DOM flickering.

Here’s the final version of our carousel:

See the Pen Using GSAP to Animate Bootstrap 3 Carousels (Part 4) by SitePoint (@SitePoint) on CodePen.

Conclusion

In this article, we went through the process of animating Bootstrap carousels with GSAP. You can find all the demos for this article in this Codepen collection. Without doubt, we covered a lot of ground but hopefully you enjoyed what we built and got an idea of GSAP’s power! Happy tweening!

Questions? Comments? Are you using GSAP in your projects? I’d love to hear from you in the comments below.

This article was peer reviewed by Joan Yinn and Yaphi Berhanu. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!

Recommended
Sponsors
The most important and interesting stories in tech. Straight to your inbox, daily. Get Versioning.
Login or Create Account to Comment
Login Create Account