In a previous post, I covered the process of converting a Bootstrap carousel into a full-screen carousel with a random initial image. In this article, I’ll build on that and cover the art of animating Bootstrap carousels, drawing on the assistance of GSAP (GreenSock Animation Platform), a popular JavaScript library.

Before going any further, let’s look at what we’ll be building.

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 for Animating Bootstrap Carousels </ title > < link rel = " stylesheet " type = " text/css " href = " https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css " > </ head > < body > ... < script src = " https://code.jquery.com/jquery-3.2.1.slim.min.js " > </ script > < script src = " https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js " > </ script > </ body > </ html >

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 " > < div class = " carousel-item first active " id = " 1 " > </ div > < div class = " carousel-item second " id = " 2 " > </ div > </ div > </ 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

we give them different background colors.

The associated CSS rules:

.item { height : 100 vh ; } .first { background : #D98F4F ; } .second { background : #2c9cae ; }

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 = " carousel-item first active " id = " 1 " > < div class = " carousel-caption " > < div class = " container " > < div class = " row justify-content-md-center " > < div class = " col " > < h2 class = " title " > </ h2 > < p class = " desc " > </ p > < ul class = " list " > </ ul > </ div > < div class = " col " > < div class = " pc-wrapper " > < img class = " pc " src = " IMG_PATH " alt = " " width = " " height = " " > < div class = " price " > </ div > </ div > < img class = " keyboard " src = " IMG_PATH " alt = " " width = " " height = " " > < button type = " button " class = " btn btn-danger btn-lg " > </ button > </ div > </ div > </ div > </ div > </ div >

If you’re following along, be sure to replace IMG_PATH with something sensible.

Here’s what the result looks like:

Building the Second Slide

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

< div class = " carousel-item second " id = " 2 " > < div class = " carousel-caption " > < div class = " container " > < h2 class = " title " > < span > </ span > </ h2 > < div class = " row justify-content-md-center " > < div class = " col cms-wrapper " > < div class = " cms " > < div class = " front " > </ div > < div class = " back " > < img class = " img-fluid " src = " IMG_PATH " alt = " " > </ div > </ div > < p class = " info " > </ p > </ div > </ div > < div class = " source " > </ div > </ div > </ div > </ div >

And its visualization:

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 ) { if ( e . which === 39 ) { $carousel . carousel ( "next" ) ; } else if ( e . which === 37 ) { $carousel . carousel ( "prev" ) ; } } ) ;

So far, we’ve built a basic carousel which supports keyboard navigation.

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. If you’re looking for a thorough introduction to GreenSock, check out GreenSock for Beginners: a Web Animation Tutorial (Part 1).

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.

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.

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

Animating Bootstrap Carousels: 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:

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:

create a TimelineLite add tweens to the timeline using its to, from, and staggerFrom methods return the timeline.

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

The DOM element that we want to animate. The duration of the animation in seconds. 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. 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) ).

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) ).

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: Using GSAP to Animate Bootstrap Carousels (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:

The slide.bs.carousel and slid.bs.carousel events that Bootstrap provides. 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:

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:

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

Animating Bootstrap Carousels: 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:

create a TimelineLite add tweens to the timeline using its from and staggerFrom methods 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.

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 ( 40 px ) ; }

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.

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 ).

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: Using GSAP to Animate Bootstrap Carousels (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: Using GSAP to Animate Bootstrap Carousels (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:

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 : 5 px ; left : 50 % ; transform : translateX ( -50 % ) ; padding : 7 px ; border : 5 px 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 >

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

Here’s the final version of our carousel:

See the Pen Bootstrap Carousel with GSAP Animations 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!

If you’ve got the basics of Bootstrap under your belt but are wondering how to take your Bootstrap skills to the next level, check out our Building Your First Website with Bootstrap 4 course for a quick and fun introduction to the power of Bootstrap.