JavaScript
Article

Creating Scroll-based Animations using jQuery and CSS3

By Simon Codrington

Creating movement is great way to provide an interesting and interactive experience for your viewers. With modern sites providing a greater deal of interactivity, it’s becoming increasingly expected that even simple websites will offer some level of animation / movement to engage their visitors.

Today I will be outlining a technique that you can adapt to your web projects – triggering animations when scrolling into a pre-defined region. These animations will be created using CSS transforms and CSS transitions. We will also use jQuery to detect when the elements are visible and to add/remove the appropriate classes.

For those who want to see examples of this in action, you can jump straight to the demos.

Why Trigger Animations on Scroll?

The main reason we would want to trigger animations on scroll, is so that they activate just as the user scrolls an element into view.

We might want to fade elements in, or provide an interesting transformation and these would only make sense when the user can actually view them.

Animating with CSS or with jQuery?

There are pros and cons to each approach. jQuery (read JavaScript) allows you to animate things that CSS doesn’t (such as the scroll position, or an element’s attributes), whilst CSS animations can be very attractive for developers who prefer putting all of their animation and presentation logic in the CSS layer.

I will be using transformations via CSS, however there are always variables to consider depending on your situation. I would take the following factors into account:

Browser Compatibility

Since our solution will be based on transformations, our browser compatibility will be limited to those that support either 2D transformations or 3D transformations.

All modern browsers will support 3D transforms and several of the older legacy browser such as Internet Explorer 9 and Opera 11.5 will support 2D transforms. Overall support for both desktop and mobile browsers is comprehensive.

jQuery’s animate method works in any (sane) browser, provided you are using the 1.X version of the library. jQuery 2.X removed support for IE8 and below, so only use this if you don’t need to support legacy browsers (lucky you!).

Speed

We want fast and smooth animations, especially when it comes to mobile devices. As such its always best to use transitions and transformations where possible.

The examples will use 3D transforms with 2D fall-backs for older browsers. We want to force hardware acceleration for speed, so a 3D transformation is a must (we will be using translate3d along with other functions that cause GPU accelerated rendering).

jQuery’s animate method is considerably slower than a GPU assisted transformation, so we will just be using jQuery for our event handling / calculations, not for our animation itself (as we want them to be as smooth as possible).

Side Note

We all know that jQuery !== JavaScript, right? Well, it turns out that using vanilla JS for animations might not be such a bad an idea after all. Whilst that is beyond the scope of this tutorial, here are two excellent articles on the subject for those who are interested in finding out more:

Now back to the show …

Detecting Animation Elements in View

The overall point of this technique is to look through all of our elements we marked as animatable and then determine if they are currently within the viewport. Let’s step through how we will achieve this:

Selector Caching

Scrolling is an expensive business. If you attach an event listener to the scroll event, it will fire many times over whenever a user scrolls the page. As we will be calling our dimension / calculation functions whenever a user scrolls, it is a good idea to store the elements returned by our selectors in variables. This is known as selector caching and avoids us querying the DOM over and over again.

In our script we will be referencing both the window object and the collection of elements we want to animate.

//Cache reference to window and animation items
var $animation_elements = $('.animation-element');
var $window = $(window);

Notice the dollar sign in front of the variables. This is a convention to indicate that they hold a jQuery object, or collection of objects.

Hooking into the Scroll Event

Next, we create our event handler that listens for the scroll event. This will fire when we scroll the page. We pass it a reference to our check_if_in_view function (which we’ll get to in a minute). Every time the scroll event is fired, this function will be executed.

$window.on('scroll', check_if_in_view);

Handling Resizing

Because we are calculating heights and widths we need to factor in orientation changes along with general resizing.

We can update our event handler to listen for both the scroll and resize events. This will enable our detection function to work when we resize or change orientation.

$window.on('scroll resize', check_if_in_view);

In addition, we also use the jQuery trigger method to trigger a scroll event as soon as the DOM is ready. We do this so that if any of the elements which should be animated are within the viewport, they will be detected as in view and the animation applied as if we had scrolled.

$window.trigger('scroll');

Scroll Position Detection

The actual detection portion of this example comes from the following script.

function check_if_in_view() {
  var window_height = $window.height();
  var window_top_position = $window.scrollTop();
  var window_bottom_position = (window_top_position + window_height);

  $.each($animation_elements, function() {
    var $element = $(this);
    var element_height = $element.outerHeight();
    var element_top_position = $element.offset().top;
    var element_bottom_position = (element_top_position + element_height);

    //check to see if this current container is within viewport
    if ((element_bottom_position >= window_top_position) &&
        (element_top_position <= window_bottom_position)) {
      $element.addClass('in-view');
    } else {
      $element.removeClass('in-view');
    }
  });
}

Lets break down what is happening here.

The check_if_in_view function is called initially when the DOM is ready and then every time we resize or scroll.

We get the current height of the window, along with its top and bottom position so we know what area we are looking at.

We go through and look for all items that will be animating in (saved in the $animation_elements variable). For each of these elements we collect its height along with its top and bottom position (so we know where it lives on the page).

We compare each item to see if its bottom position is greater than the top position of the window but also that the item’s top position is less than the bottom position of the window.

Here is a visual example

Detect if element is within viewport

Calculating the Height and Width

In our detection function we need to get the heights and positions of various elements to calculate things correctly, this is where we have used jQuery’s height functions. It’s important to have a breakdown of how these height functions work

height() and width()

The height() and width() functions return the height or width of an element. They exclude all padding, borders and margins.

jQuery height and width example

For a full breakdown visit the height or width documentation.

innerHeight() and innerWidth()

The innerHeight() and innerWidth() functions return the height or width of the element including its additional padding (however it excludes both borders and margins)

jQuery innerHeight and innerWidth example

For a full breakdown visit the innerHeight or innerWidth documentation.

outerHeight() and outerWidth()

The outerHeight() and outerWidth() functions return the height or width of the element and include its padding and border.

In addition you can also specify to include its margins by passing a value of true to the function.

jQuery outerHeight and outerWidth example

For a full breakdown visit the outerHeight or outerWidth documentation

Scroll Animation Examples

Listed below are a series of animations that use the basics of what we have discussed. These examples will look for animation elements and apply the active in-view class when they’re within the viewport.

Elements that you want to move should all have a standard class such as animation-element that sets its position to be relative or absolute. In addition, if you are going to create multiple effects you can create corresponding classes such as slide-left which can be combined with the in-view class. You should then apply the transformation to a class such as animation-element.slide-left.inview

Slide in from Left

For our first example we will be sliding in elements from the left when they enter the viewport. We achieve this by using a translate3d on our elements x axis.

See the Pen CSS Animations on Scroll – Slide in From Left by SitePoint (@SitePoint) on CodePen.

In this example we have used it to display staff profiles, but you can re-leverage the same functionality to slide in any elements you need.

Fade in from Bottom

This time we will be fading our elements from the bottom upwards as the user scrolls. We achieve this via a translate3d on the element’s y axis.

For this example I’ve listed course information about topics in a grid structure. When the user scrolls down, each card in view will fade in and move up, displaying information about the course.

See the Pen CSS Animations on Scroll – Fade From Bottom up by SitePoint (@SitePoint) on CodePen.

Multi-Step Bouncing Animation

For our final example we’ll use a multistage animation. To do this, we’ll define custom keyframe animations that combine a rotation with a translation. This type of animation can help showcase areas of your website (for this example we are showcasing staff member profiles).

See the Pen CSS Animations on Scroll – Multi Step Move by SitePoint (@SitePoint) on CodePen.

Where to from Here?

From here you can take the concepts you have learned and apply them to your projects.

Now that you can detect when an element is in view you can chain additional transformations or effects to create interactive interfaces. For example when an element enters the viewport (and after its transformation) you can then transform additional elements such as fading in a title, scaling in an image etc.

Are you already using these effects in your projects? Or do you think that animations are overused and detract from the user experience? Either way I’d love to hear from you in the comments.

  • Mr.Key

    Nice article. I was using animate.css and WOW.js without learning how it actually works. http://mynameismatthieu.com/WOW/

    • simon codrington

      Thanks @mrkey:disqus for the comment and the link :)

      I’ve used similar libraries before, but now that I understand how it all works its easier to make more intricate movements and functionality now.

  • eddie404

    Interesting article – thanks Simon. I have been avoiding animation because of browser compatibility and, truth be known, an element of ignorance on my part. I have already bookmarked the page and started testing. Thanks.

  • http://www.walabbady.com Waleed Alabbady

    WOW.js :)

  • Deepanshi Gupta

    how can I get complete code of last two animated images since I don’t know about js

    • simon codrington

      Hey @deepanshigupta:disqus what are you trying to do. Which last two images are you referring to?

  • Jose Manuel Mujica Puentes

    Thank you Simon, this is a great guide.

    • simon codrington

      Hey @josemanuelmujicapuentes:disqus thanks for the feedback :)

      Hopefully it will help you to build some fancy animations!

  • Daniel

    Hi – I am struggling with this a little bit as I have no understanding of Jquery and javascript. I have been trying to add this to my website for days but can never manage to get the on scroll to work. any chance of an idiot’s guide? Where do I place these scripts? In the head section ? And do I have to install anything on my website for it to work?

    • simon codrington

      Hey there Daniel, sorry for the late reply (notifications were off).

      Let me know if you’re still struggling with this and i’ll see what I can come up with :)

  • Sunny Israni

    HI Simon–this guide is beyond amazing! I just have a question though, what about tying the speed of the transitions to the speed at which the user is scrolling–I’m finding many websites these days that do just this, and havent the slightest idea how they go about doing it. I’m assuming this type of animation would be heavy on the JS/JQuery side, correct?

    • simon codrington

      Thanks for the feedback @sunnyisrani:disqus Glad to see that you found my article useful :)

      If you wanted to adjust the speed at which elements animate in the approach would be different depending on how you are animating.

      If you are using ‘transitions’ (as in just left: -100% to left 100%) you would have to change the ‘transition-duration’ to either a higher or slower number depending on scroll speed (i..e on a slow scroll we can transition in 1000ms, on a fast scroll we transition quickly in 300ms)

      In my example the `check_if_in_view()` function is called when we ‘scroll’. Inside here to detect the speed we would have to check the previous position we were at and then compare it to our current position we scrolled to. If we jumped a big distance we can assume we are scrolling really fast (this would be a process of trail and error and might be tricky to set up)

      If you are using ‘keyframe animations’ it would be harder as you would have to calculate the speed you want and then re-write the css animation. For example one of the animations is: animation: left_animation 1300ms ease-in both; To change this you would have to detect the speed and then re-write the rule dynamically i.e animation: left_animation 955ms ease-in both; (on a faster scroll)

      You would also need to add the vendor prefix versions like ‘-webkit-transform’ also.

      Changing the timing on that would mean potentially you re-declare the new animation dozens of times as you scroll (which may impact performance, I’m not 100% sure how badly though)

      It’s all possible but I think it would be a heap of testing to get it correct :)

      Hopefully some of this helps

  • zulius akbar

    thanks :) very help me,

    • simon codrington

      No worries, glad the article was useful for you :)

  • Rhomy Prama Design (Web Design

    Thanks, its realllyy help me. God Bless You, keep sharing. :)

  • Susheel Sundar

    Simon which js libraries we should add to make this work?

  • simon codrington

    Hey Bradley. I think what you could do is detect the movement direction and apply a different class. For example when you’re in the scroll zone you could add a ‘scroll-forward’ class if you’re scrolling down or a ‘scroll-backwards’ class if you are scrolling up.

    You could then create 2 different animations, one for scrolling something left to right and the other right to left, mapped to those two different classes.

    You could compare the current scroll position to the previous scroll position and check to see if it’s greater or less than what it was before (which would tell you what was you’re scrolling)

    • John Huber

      would you be willing to indicate what line of the JS the scroll UP or scroll DOWN separation would occur?

  • http://www.bijudesigner.com/ Biju UI Developer: www.bijudes

    Cool <3

    • simon codrington

      Thanks for the feedback, glad you enjoyed the article!

  • Joe

    Hi Simon,

    I’m currently working on the final details of my first website, it’s based on Bootstrap and some JS. I’m trying to implement some CSS animations as you explained (which by the way, this was an awesome article, very well explained) but they are not triggering on scroll, though they are on browser’s window resizing. You think Bootstrap is having some issues with it?

    • Joe

      It’s acting very inconsistent, I added a parenthesis and semicolon (testing some things) and when I removed them now it doesn’t even triggers on window re-sizing.

      • simon codrington

        Hey there @IndependentOpinionGiver:disqus

        Seems pretty strange that you’re getting mixed results.

        It could be that something is hijacking the scroll event and it’s not even firing (but the resize event still is)

        If you have a development site I might be able to have a look to see if I can spot anything.

        Cheers!

  • simon codrington

    Glad you liked it @disqus_zoY6Vdqn8K:disqus. Thanks for having a read. Hopefully you can make something awesome with this in mind :)

    • Syed Arif Iqbal

      Yes done. But i learn about animate.css, transition event and techniquea by u

  • http://benjamingraham.com.au Ben Graham

    This really Helped, Thank you ;)

    • simon codrington

      Glad you enjoyed it mate!

      • http://benjamingraham.com.au Ben Graham

        Cheers Simon!

  • http://benjamingraham.com.au Ben Graham

    Quick Questions? I’m wanting to fire different animation on different classes. (A) is targeting a div rather than class faster or better? (B) How do I do this within one code block I don’t want to repeat code and I would like to say have a fadein on my blog posts (one class) and a left animation on my top content(another class on a div) ?

    • simon codrington

      Hey there Ben

      You can use the same code overall to add an active state to your element (and with this class you could target a different animation, I.e when element A is active you could trigger one animation that fades in and with element b use a slide in animation)

      Something like

      .my-element-a.in-view{
      transform: SlideAnimation;
      }

      .my-element-b.in-view{
      transform: FadeAnimation;
      }

      You use the ability of this script to add the ‘in-view’ class and then target different animations

      In regards to performance there isn’t much speed benefit in targeting elements (like just a div) or using classes (there might be some noticeable difference when used with thousands of elements though)

      Hopefully this helps!

      • http://benjamingraham.com.au Ben Graham

        Great! Thanks for the quick reply, the most usefull and best practice animation tute I’ve com accross… I was setting a set pixel height initially…. onScroll …..but it’s not the best way to do it! Cheers

  • John Huber

    I am doing something wrong, the JS never adds the in-view class so the animated elements remain hidden. I copied the JS as it was listed above in your ‘slide from the left example’. I am using jQuery v1.11.2. Any thoughts what is wrong?

    • http://benjamingraham.com.au Ben Graham

      Have you wraped the code in script tags and targeted the correct outer container of the element you would like to animate…. With a class or ID?

      • John Huber

        “wrap the code in script tags” … that was the mistake i made. Thank you Ben!

        • http://benjamingraham.com.au Ben Graham

          No worries! It’s common for tuts and devs to ommit this from their…… Same with most languages used for different tuts… I.e for php tuts…. You could also inject a script in the footer pointing to your code on the server or using a cdn which has it’s advantages

  • Bishwambhar Khadka

    This really helped a lot saving time. Thank you very much, Keep it up !

    • simon codrington

      Thanks Bishwambhar. Happy you enjoyed the article :)

  • John Huber

    Is there a way to stop the animations on scroll UP? so elements remain visible/in place.

    • John Huber

      So, i know that this is only a hack solution, but if line #23 of the JS is hidden, then the in-view classname won’t be removed, which means the animation only runs once.

      JS coded out:
      // $element.removeClass(‘in-view’);

      • simon codrington

        Yep, exactly it. You can either remove that whole section or the `else` section that contains it if you only want animations to run once :)

    • Hassan

      Just tried this and it seems to work. After the original ‘if’ statement but just before the ‘else’, you can put:
      else if ((element_bottom_position < window_top_position) && (element_top_position <= window_bottom_position)) {
      $element.addClass('in-view');
      }
      then it will only animate on scroll down and not on scrolling back up. Basically its saying that for elements which are out and above of the viewport should still maintain the class .in-view and hence not de-animate themselves.

  • Gino Van Geersom

    Thanks a lot for sharing this. You saved me a lot of time. Everything was explained very well.
    By the way, there are a few examples like in ‘Calculating the height and width’ which don’t show up. Maybe some broken links?

    • simon codrington

      Hey @ginovangeersom:disqus thanks for commenting. I’ve had a look and they look like they are coming through (They come from cloudfront). Are you able to see these?

  • John Huber

    Is there a way to alter the JS so that the in view function has a slight delay when the animated objects appear on screen? Like a buffer either in height or time?

    • simon codrington

      hey there @disqus_Gn30Q0AnQb:disqus You should be able to add a delay by adding it as a property e.g. ‘transition-delay: 300ms’. You could do this on your active item (by editing the class that is added when your item scrolls into view).

      Doing this would mean that your animation is triggered after X ms when it enters the scrolling region.

      You could also adjust when the active class is added to the item by playing with the JS (but that would be more involved that just adding a transition delay)

      Let me know if that helps you! (and sorry for the delay in getting back to you)

      • John Huber

        no apology needed for delay, thank you for taking the time in replying! I will try the delay, thank you

  • Evgeniy Kureyko

    Hi! When im scrolling on first element, all other ellements what are hiden are also triggered.

    • simon codrington

      Howdy. Can you remove any other complexities in your site to just the bare basics of content + this scrolling library? I’ve double checked and this works well on CodePen + my various WordPress websites I’ve built.

      Cheers

  • simon codrington

    Hi there, sorry for the very late reply.

    The JS / CSS that are used in the CodePen / article should work just fine on any platform. The only requirement would be ensuring that jQuery is loaded first and that all of your classes are being added as you scroll.

    Is anything working for you?

  • Pranav Raghaw

    @simoncodrington:disqus

    hey buddy , I made every effort to get the last slide-on-scroll effect on my website ,i.e.,multi-step bouncing effect. But the animation isn’t appearing, only the blank screen appears. there is no problem in the browser version, or code. Please, can you give the compiled code for the last slide-on scroll effect. It would be a great help.

  • Pranav Raghaw

    is there a way that the animated elements enter once and then don’t disappear, I mean they stay there, not appearing again-n-again whenever a user scrolls to the region.@simoncodrington:disqus
    please help

    • simon codrington

      Hey Pranav Raghaw sorry for the lag. You should be able to make the elements stay there by removing the vanishing code.

      “`
      //check to see if this current container is within viewport
      if ((element_bottom_position >= window_top_position) &&

      (element_top_position <= window_bottom_position)) {

      $element.addClass('in-view');

      } else {

      //remove me / the else block here
      //$element.removeClass('in-view');

      }
      “`

      If you do this then it will appear only once and stay when you scroll back up and down.

  • fitsticky

    Awesome. Thanks.

  • simon codrington

    Hey there Clare thanks for reading. Can you explain what you’re looking for?

  • Ashwani Patel

    thanks you Its really works And also helpfull

  • Gloire

    Just what i needed. Very well explained. Great post Simon.

    • simon codrington

      Thanks Gloire. Happy that my article has helped you out :)

  • kobachi

    This can be much, much more performant if you cache the element offsets (assuming you’re not moving them around) rather than reading them every time the user scrolls.

  • Raul

    Hi This works great thanks!!
    But I have an issue I did copy your CSS but it is not aligned properly the third Testimonial is coming in place of fourth one and the place of third is blank

  • Sal

    @simoncodrington Thank you for the great tutorial, but i’m somehow stuck. i’ve done it exactly the same way, but the code does not work on my project. It does not fade in.

    would you mind taking a look?

    http://codepen.io/salman15/pen/rLRZrJ

  • Layton Miller

    No, @kobachi:disqus is right. Inside every single loop of the each() loop you’re calling element.offset(). That’s a function call, and will execute every single time. What he’s saying is, you perform that call outside the loop at bootstrap time and on resize, and don’t call it again otherwise. Calls like that can be expensive, particularly if they cause a document reflow:
    https://gist.github.com/paulirish/5d52fb081b3570c81e3a
    Causing a reflow for every single iteration of the loop is extremely expensive, and compounds as your loop grows :(
    This of course only works if you know you don’t have any resizing elements on the page (like expanding/collapsing boxes), which would make it more complicated. Still doable, but more complicated.

    • Guillaume Veyrat

      Hi, your comment is very interesting. I am now wondering how to properly prevent the jQuery from calling the function again everytime the user scrolls up. I think that I must use a boolean so that one it’s True, the function doesn’t run again, but my skills are not that good and I’m wondering if you could help me.
      Thank you in advance for you help, and have a nice day!

  • James

    Great article and easy to follow. Thanks Simon!

    • simon codrington

      Thanks @disqus_7UOLxwyaIV:disqus , Glad you liked it!

  • Joe Cassler

    Hi Simon, this is awesome stuff; is there a way to make it so that the animations only occur upon scrolling down and not when scrolling back up?

Recommended

Learn Coding Online
Learn Web Development

Start learning web development and design for free with SitePoint Premium!

Get the latest in JavaScript, once a week, for free.