We’ve covered the fundamentals of GreenSock and moved a few boxes using TweenLite and TimelineLite in the previous GreenSock tutorials.
But lets be honest, how often do you need to move a box on your real life projects?
In this tutorial you’ll learn how to create a simple image slider using GreenSock’s TweenLite and TimelineLite.
The effects and animations in this tutorial are inspired by 2 great websites – heartkids.co.nz and fixedgroup.com, credit to the talented teams behind them.
What you’ll learn
- How to structure HTML and CSS for a fullscreen layout
- How to play GreenSock timelines based on user interaction
- How to create a cool tilt effect
Video Screencast
Do you prefer to watch videos instead of following written tutorials? You can watch this tutorial on my YouTube Channel.
Ready to get your hands dirty with GreenSock?
Note: This tutorial is aimed at designers and developers with a working HTML/CSS knowledge and the primary focus is to learn how to use GreenSock in a real world project.
I also assume that you already know what GreenSock is.
1. HTML – Setup A Structure For Fullscreen Image Slideshow
Firstly we’ll create our structure in the index.html
.
<div class="bcg"> <div class="hero"> <!-- Top part --> <div class="top"> <a href="https://ihatetomatoes.net/blog/" class="logo"><img src="img/icon-logo.png" /></a> <!-- Slide 1 top content --> <div class="homeSlide slide01 active"> <h1>Green<span>Sock</span> Tilt Effect</h1> </div> <!-- Slide 2 top content --> <div class="homeSlide slide02"> <h1>Split Screen Slider</h1> </div> <!-- Slide 3 top content --> <div class="homeSlide slide03"> <h1>Slide 3 Title</h1> </div> </div><!-- /Top part --> <!-- Bottom part --> <div class="bottom"> <!-- Slide 1 bottom content --> <div class="homeSlide slide01 active"> <p> Move your mouse to see the tilt effect in action.<br /> <em>Your current position is: <strong>0, 0</strong></em> </p> </div> <!-- Slide 2 bottom content --> <div class="homeSlide slide02"> <p> Another slide description goes here. </p> </div> <!-- Slide 3 bottom content --> <div class="homeSlide slide03"> <p> Another slide description goes here. </p> </div> </div><!-- /Bottom part --> <!-- Fancy divider with gradient backgound --> <div class="divider"></div> </div> <!-- Prev/Next Navigation --> <div id="slideNav"> <ul> <li class="slideNavPrev"> <a href="#" title="Go to previous slide"> <span class="ico ico-up">↓</span> </a> </li> <li class="slideNavNext"> <a href="#" title="Go to next slide"> <span class="ico ico-down">↑</span> </a> </li> </ul> </div> </div>
We’ll create a fullscreen container .bcg
and inside of it .hero
container that will be used for the tilt effect.
.top
and .bottom
are 2 containers with position: absolute
, both have the left and right offset 30px
, but their bottom and top offset is set to 50%
.
This makes them cover only half of the screen. Here is the visual image of our html layout.
2. CSS – Create A Fullscreen Layout
Copy and paste the CSS for our sturcture into the main.css
.
.logo { position: absolute; top: 50px; left: 50%; -webkit-transform: translateX(-50%); -ms-transform: translateX(-50%); transform: translateX(-50%); z-index: 6; opacity: 0.3; } .bcg { position: fixed; top: 0; left: 0; right: 0; bottom: 0; } h1 { color: #ffffff; font-weight: 300; position: absolute; bottom: 0%; width: 100%; margin: 0 0 20px 0; font-size: 60px; text-align: center; } .hero { position: absolute; height: 100%; width: 100%; } .top { overflow: hidden; position: absolute; top: 30px; left: 30px; right: 30px; bottom: 50%; border: 1px rgba(255, 255, 255, 0.2) solid; border-width: 1px 1px 0 1px; } .homeSlide { position: absolute; width: 100%; height: 100%; } .homeSlide.slide01 { background: url(../img/img_clouds-top.jpg) no-repeat bottom center; background-size: cover; } .bottom .homeSlide.slide01 { background: url(../img/img_clouds-bottom.jpg) no-repeat top center; background-size: cover; } .homeSlide.slide02 { background: url(../img/img_clouds-top2.jpg) no-repeat bottom center; background-size: cover; } .bottom .homeSlide.slide02 { background: url(../img/img_clouds-bottom2.jpg) no-repeat top center; background-size: cover; } .homeSlide.slide03 { background: url(../img/img_clouds-top3.jpg) no-repeat bottom center; background-size: cover; } .bottom .homeSlide.slide03 { background: url(../img/img_clouds-bottom3.jpg) no-repeat top center; background-size: cover; } .divider { z-index: 3; position: fixed; top: 50%; margin-top: -40px; left: -200px; right: -200px; height: 40px; background: -webkit-linear-gradient(bottom, rgba(0, 0, 0, 0.5) 0%, rgba(34, 34, 34, 0) 100%); background: linear-gradient(to top, rgba(0, 0, 0, 0.5) 0%, rgba(34, 34, 34, 0) 100%); } .bottom { overflow: hidden; position: absolute; top: 50%; left: 30px; right: 30px; bottom: 30px; border: 1px rgba(255, 255, 255, 0.2) solid; border-width: 0 1px 1px 1px; } .bottom p { font-size: 20px; color: #ffffff; font-weight: 300; opacity: 0.7; text-align: center; } .bottom a { color: #fff; } #slideNav { z-index: 5; position: fixed; right: 50px; top: 50%; width: 50px; -webkit-transform: translateY(-50%); -ms-transform: translateY(-50%); transform: translateY(-50%); } #slideNav ul { list-style: none; color: #ffffff; font-size: 13px; text-align: center; margin: 0; padding: 0; } #slideNav li { width: 50px; height: 50px; line-height: 50px; background-color: rgba(0, 0, 0, 0.7); margin-bottom: 1px; } #slideNav a { display: block; width: 50px; height: 50px; position: relative; overflow: hidden; text-decoration: none; color: #ffffff; }
.divider
is a 40px
tall container centered on the page and sitting inside of the .hero
on top of .top
and .bottom
.
Lets now break down the top part of our slider.
It contains 3 homeSlide
containers with a unique class slide01
, slide02
and slide03
and a unique background image.
If you save the page now and preview it in a browser you’ll see the slide03
sitting on top.
That’s ok, we’ll use GreenSock later to hide all slides apart from the .active
slide.
Similar structure, classes and styles are applied to the .bottom
half of our slider.
Now it’s time to start tweening with GreenSock.
3. Go To The Next Slide
All the interactivity will happen in the main.js
, open the file and keep adding the following code.
Don’t worry if you’ll make a mistake, the whole code is at the bottom of this part of the tutorial.
3.1 Create variables
// Setup variables var $activeSlide = $(".active"), $homeSlide = $(".homeSlide"), $slideNavPrev = $(".slideNavPrev"), $slideNavNext = $(".slideNavNext") $slideNavPrevA = $(".slideNavPrev a"), $slideNavNextA = $(".slideNavNext a"), $hero = $(".hero");
These variables are all the elements on the page that we will be interacting with. We are simply targeting each element by their class
or tag
eg. ".slideNavNext a"
.
$homeSlide
are all the moving parts of our image slideshow, these are both top
and bottom
parts of all 3 slides.
$activeSlide
is the currently active slide.
If you are a beginner or want to refresh your jQuery knowledge, read this jQuery Datatypes and Selectors guide.
3.2 Hide all inactive slides on page load
// Init function that run on page load function init(){ // Hide all slides apart from the active one TweenLite.set($homeSlide.not($activeSlide), {autoAlpha: 0}); // Disable arrow down on page load TweenLite.set($slideNavPrev, {autoAlpha: 0.2}); } // Run Init function init();
The first tween inside of the init function will hide all slides apart from the one with a class .active
.
The second tween will fade out the arrow pointing down because at that moment there is no previous slide.
3.3 Run goToNextSlide() function on click
Now we’ll setup a function that we want to be triggered when the user clicks on the $slideNavNext
button.
Add this after the init();
and refresh the page.
// Go to next slide function goToNextSlide(){ alert('Go to next'); } // Navigation click - go to the Next Slide $slideNavNext.click(function (e) { e.preventDefault(); goToNextSlide(); });
You’ll see that a click on the arrow up now triggers an alert popup with “Go to next” message.
3.4 Animate The Right Slides
We can remove the alert inside of goToNextSlide()
and update the click function to pass through the slideOut
and slideIn
.
$slideNavNext.click(function (e) { e.preventDefault(); var slideOut = $('.homeSlide.active'), slideIn = $('.homeSlide.active').next('.homeSlide'); goToNextSlide(slideOut, slideIn); });
Everytime the user clicks on the navigation, we want to pass the right slideOut
and slideIn
elements.
This means that on a page load the slideOut is .slide01
and slideIn is the next slide .slide02
.
3.5 Go To Next Slide – GreenSock Timeline
Now we’ll update the goToNextSlide
function to accept the 2 new parameters.
// Go to next slide - pass 2 parameters - slideOut and slideIn function goToNextSlide(slideOut, slideIn){ var tl = new TimelineLite(), slideOutH1 = slideOut.find('h1'), slideOutP = slideOut.find('p'), slideInH1 = slideIn.find('h1'), slideInP = slideIn.find('p'), index = slideIn.index(), size = $('.top .homeSlide').length; // go to the next slide timeline }
We are also setting up new variables inside of this function.
tl
is a new GreenSock TimelineLite that will be used for our animation sequence.
Then we are getting h1
and p
of both slideIn and slideOut so we can animate them in different directions.
index
and size
will be used for a few if statements later on.
Note: paste the following code inside of the goToNextSlide
function, where indicated by the comment.
// go to the next slide timeline tl // move the new slide (the one about to enter viewport) out of the viewport and add class active .set(slideIn, {y: '100%', autoAlpha: 1, className: '+=active'}) // remove class active from the currently active slide (slideOut) .set(slideOut, {className: '-=active'}) // animate H1 and p of the active slide up and fade them out .to([slideOutH1,slideOutP], 0.3, {y: '-=15px', autoAlpha: 0, ease:Power3.easeInOut}, 0) // animate active slide up (out of the viewport) .to(slideOut, 0.5, {y: '-100%', ease:Power3.easeInOut}, 0) // animate new slide up (from out of the viewport) .to(slideIn, 0.5, {y: '-=100%', ease:Power3.easeInOut}, 0) // animate H1 and P of the new slide up and fade them in .fromTo([slideInH1,slideInP], 0.3, {y: '+=20px', autoAlpha: 0}, {autoAlpha: 1, y: 0, ease:Power1.easeInOut}, 0.3);
Read the comments above each of the tweens to see what they are doing.
Firstly we are positioning the new slide slideIn
out of the viewport and adding .active
to it and removing class from the slideOut
.
The main image tweens are highlighted in green and both of them are happening at the same time as the text is fading out.
They all start at the absolute beginning of our timeline.
Read more about positioning of tweens on the GreenSock timeline.
If you are following correctly, you’ll see that we have an issue when we click on the next arrow on the third slide, we are going to a blank slide.
3.6 Prevent Going To A Blank Slide
To avoid the transition into a blank slide (essentially there is no next slide when we are on the .slide03
), we can simply wrap our timeline into an if statement.
if(slideIn.length !== 0){ // go to the next slide timeline tl // Keep tweens, just wrap the timeline in the highlighted if statement } // Fade out arrow up and fade in arrow down
This will check whether there is a next slide and if there is, we will play our timeline.
3.7 Fade In/Out Navigation Arrows
To make this function complete we will fade out the arrow up
on the last slide and ‘enable’ arrow down on the first click.
// Fade out arrow up and fade in arrow down // Fade in arrow down TweenLite.set($slideNavPrev, {autoAlpha: 1}); // Fade out arrow up on last slide if(index === size){ TweenLite.to($slideNavNext, 0.3, {autoAlpha: 0.2, ease:Linear.easeNone}); }
index
returns the index value of the new slide (.slide01
= 0, .slide02
= 1 etc) and size
is a total count of slides in our case 3
.
Both of these variables return 3
on the last slide, that is when we want to fade out the arrow up.
This was a beast but creating the opposite direction function will be much easier.
3.8 All together
To make sure you’ve added the code to the right places, here is the complete code for the transition to next slide.
// Go to next slide - pass 2 parameters - slideOut and slideIn function goToNextSlide(slideOut, slideIn){ var tl = new TimelineLite(), slideOutH1 = slideOut.find('h1'), slideOutP = slideOut.find('p'), slideInH1 = slideIn.find('h1'), slideInP = slideIn.find('p'), index = slideIn.index(), size = $('.top .homeSlide').length; if(slideIn.length !== 0){ // go to the next slide timeline tl // move the new slide (the one about to enter viewport) out of the viewport and add class active .set(slideIn, {y: '100%', autoAlpha: 1, className: '+=active'}) // remove class active from the currently active slide (slideOut) .set(slideOut, {className: '-=active'}) // animate H1 and p of the active slide up and fade them out .to([slideOutH1,slideOutP], 0.3, {y: '-=15px', autoAlpha: 0, ease:Power3.easeInOut}, 0) // animate active slide up (out of the viewport) .to(slideOut, 0.5, {y: '-100%', ease:Power3.easeInOut}, 0) // animate new slide up (from out of the viewport) .to(slideIn, 0.5, {y: '-=100%', ease:Power3.easeInOut}, 0) // animate H1 and P of the new slide up and fade them in .fromTo([slideInH1,slideInP], 0.3, {y: '+=20px', autoAlpha: 0}, {autoAlpha: 1, y: 0, ease:Power1.easeInOut}, 0.3); } // Fade out arrow up and fade in arrow down // Fade in arrow down TweenLite.set($slideNavPrev, {autoAlpha: 1}); // Fade out arrow up on last slide if(index === size){ TweenLite.to($slideNavNext, 0.3, {autoAlpha: 0.2, ease:Linear.easeNone}); } } // Navigation click - go to the Next Slide $slideNavNext.click(function (e) { e.preventDefault(); var slideOut = $('.homeSlide.active'), slideIn = $('.homeSlide.active').next('.homeSlide'); goToNextSlide(slideOut, slideIn); });
4. Go To The Previous Slide
The function to control the movement in the opposite direction looks almost identical.
We will call this function goToPreviousSlide()
and will also pass through the correct slideOut
and slideIn
to it.
// Go to previous slide - pass 2 parameters - slideOut and slideIn function goToPreviousSlide(slideOut, slideIn){ var tl = new TimelineLite(), slideOutH1 = slideOut.find('h1'), slideOutP = slideOut.find('p'), slideInH1 = slideIn.find('h1'), slideInP = slideIn.find('p'), index = slideIn.index(), size = $('.top .homeSlide').length; if(slideIn.length !== 0){ // go to the previous slide timeline tl // move the new slide (the one about to enter viewport) out of the viewport (to the top) .set(slideIn, {y: '-100%', autoAlpha: 1, className: '+=active'}) // remove class active from the currently active slide (slideOut) .set(slideOut, {className: '-=active'}) // animate H1 and p of the active slide down and fade them out .to([slideOutH1,slideOutP], 0.3, {y: '+=15px', autoAlpha: 0, ease:Power3.easeInOut}, 0) // animate active slide down (out of the viewport) .to(slideOut, 0.5, {y: '100%', ease:Power3.easeInOut}, 0) // animate new slide down (from out of the viewport) .to(slideIn, 0.5, {y: '+=100%', ease:Power3.easeInOut}, '-=0.5') // animate H1 and P of the new slide down and fade them in .fromTo([slideInH1,slideInP], 0.3, {y: '-=20px', autoAlpha: 0}, {autoAlpha: 1, y: 0, ease:Power1.easeInOut}, 0.3); } // Fade in arrow up TweenLite.set($slideNavNext, {autoAlpha: 1}); // Fade out arrow down on first slide if(index === 1){ TweenLite.to($slideNavPrev, 0.3, {autoAlpha: 0.2, ease:Linear.easeNone}); } } // Navigation click - go to the Previous Slide $slideNavPrev.click(function (e) { e.preventDefault(); var slideOut = $('.homeSlide.active'), slideIn = $('.homeSlide.active').prev('.homeSlide'); goToPreviousSlide(slideOut, slideIn); });
All the tweens within this timeline have the values reversed, in other words what was animating from the bottom is now coming from the top.
We are triggering this function when the user clicks the arrow pointing down $slideNavPrev
.
5. Adding A Tilt Effect
Now we have a fully functionally fullscreen slideshow, but we will add one more subtle effect to it.
We will tilt the whole .hero
container based on the current mouse position.
Add this to the main.js
:
// Mouse move tilt effect $(document).mousemove(function(event){ // Detect mouse position var xPos = (event.clientX/$(window).width())-0.5; var yPos = (event.clientY/$(window).height())-0.5; // Tilt the hero container TweenLite.to($hero, 0.6, {rotationY:5*xPos, rotationX:5*yPos, ease:Power1.easeOut, transformPerspective:900, transformOrigin:"center"}); // Update text on the page with the current mouse position $(".bottom strong").text(event.pageX + ", " + event.pageY); });
Inside of the .mousemove
function, we are firstly detecting the current mouse position.
Based on the mouse position and the browser window width, the xPos
returns value -0.5
when we are hovering over the left edge of the viewport and 0.5
when we are hovering over the right edge, it returns zero right in the middle.
The same calculation applies to the yPos
.
Here is a visual indication of the rotation calculation.
Then we are rotating the .hero
container using a simple .to
tween – rotationY:5*xPos, rotationX:5*yPos
.
Play with the rotation multiplier 5*
and the transformPerspective
values to get the desired effect. Remember subtle effects are usually the most effective.
Note: Please also consider your target audience before implementing this tilt effect into your projects. Animate responsibly.
Conclusion
There you have it, a simple fullscreen image slideshow created using GreenSock’s TweenLite and TimelineLite.
I hope this GreenSock tutorial showed you how you could create your own custom image slideshows using this amazing animation platform.
What do you think about the final demo? Did I forget to explain something in more detail or do you have any other GSAP related questions?
Let me know in the comments below and don’t forget to subscribe to my YouTube Channel for a screencast version of this tutorial.
Other GreenSock Learning Resources
- Simple GreenSock Tutorial – Your first steps with GSAP
- GreenSock TimelineLite Tutorial
- GreenSock Workshop – Online course with over 8hrs of GreenSock Tutorials
Share this
Want To Learn More About GreenSock?
Join me in the GreenSock Workshop and learn how to build 3 interactive projects from start to finish.
Download Free Toolkit
All you need to know to get started with GreenSock and ScrollMagic, in one single package. Straight into your inbox.
I love you Petr
Haha, thanks Martin. You rock!
Pretty nice 🙂
Thanks Sylvain.
Awesome stuff, as always Petr. Keep ’em coming, it’s a great way for all of us to consolidate our growing GSAP skills with real case scenarios.
I love the attention to details you put in making these explainer images with dimensions and values. Very well crafted, makes it very clear.
In other words, you rock! 🙂
Thanks Simon, I am trying to create these images to make it easier to understand the code snippets. Glad to hear that they work:) Thanks heaps for your support Simon!
Hi Petr!
I found your tutorial really useful and the result is amazing, but I am having performance issues, when tilting the image, this moves really slowly and feels no fluid at all.
Have that ever happened to you?
Hi Eneas, do you see the same issue with my demo files? Which browser or OS are you using?
Petr this is great, thank you so much. I was wondering – how would we go about making this an endless loop, as in if you were at the beginning and clicked previous it would go to the end and similarly for the last slide clicking and going back to the beginning?
Again thanks so much, just signed up to your mailing list as a result!
Best,
Oliver
Hey Oliver, that would be quite simple.
Here’s a CodePen with the infinite loop.
All you have to do is to add an if statement checking if you are on the first or last slide, and pass the right slide to the function, eg.:
Hope that helps, and don’t forget to confirm you email, until then you are still not subscribed.
Cheers
Petr
Petr, thanks a lot for this. I am now confirmed on the list also, cheers!
No probs Oliver, let me know if you have any other GSAP questions. Cheers
Hey Petr,
I’ve been having so much fun playing around with this it’s fantastic so thanks again. Something i’m having trouble doing is creating the logic for the slider to go back to the beginning when at the end and visa versa for going back when at the beginning. I know you had previously posted this little snippit below –
which is perfect for if there are 3 slides, but I was hoping you’d be able to help with the logic to work it out regardless of the number of slides. I’m thinking you have to determine the length of the list using .length and then pass that against the current .index of the .homeSlide – but I can’t seem to get it to work! Any chance you could help out here if you have any spare seconds? Thanks so much!
Oliver
Hey Petr,
Just wondering if you had any ideas on the above request? Sorry to ask again i’ve been trying to figure this out for ages!
Thanks again for all your work.
Oliver
Hi Oliver,
if you simply use the CSS :last-child selector, it should always return the last slide, no matter how many slides are in your slideshow.
Hope that helps.
Hi Petr,
I love your tutorial I was looking for this kind of slider for months and months. You are rock. I would like to make some changes if it is possible I would like to load next slide with jquery $.load method because every slider have a subpages so when we click on link new page will load with the sliding effect with $.load method.
How can I achieve this effect. I hope it makes sense to you.
Here is a reference site. I want to achieve same effect.
http://nicolas-bussiere.fr
many thanks.
Hi Rana, thanks for the cool reference site, I see what you are trying to do. The loading of additional content via AJAX is outside of the scope of this tutorial but I might create one in the future.
Thanks Petr for your reply.
I will really really appreciate if you will make this tutorial.
Many thanks
Hi Petr,
I would like to add some more functionality in this slider, however I need your help if possible. I would like to add mouse wheel control in this slider when user scroll mouse wheel slide should change one by one with mouse wheel interaction. When slider on last slide mouse wheel stop to move slides. To better understand what I want to achieve please visit this website
http://nicolas-bussiere.fr
you will get the idea.
I asked you before one question how to load slider through AJAX. I achieved this effect. You can see live demo here. When you click on heading link it will take you another page where the slider starts.
http://magtechpro.work/ab/index6.html
Thanks in advance