Creating Scroll Animations with Waypoints and Animate.css

Last fall Atomic Object launched a redesigned website with lots of great new visuals and content. One of my favorite features of the redesign is the “slide-in reveal” homepage animation that slides images and text in from the sides of the screen as you scroll down the page.

Homepage Scroll

I was recently asked to do some updates and revisions on the website, and so took the opportunity to dig into how the animations work. It turns out that achieving the animations is relatively simple.

Here’s how you can implement your own “slide-in reveal” animations:

What You’ll Need

  • Waypoints – A javascript library that allows us to execute a function when we scroll to an element.
  • Animate.css – A CSS animation library that enables sophisticated animations through HTML classes.
  • jQuery – Although not a dependency of either Waypoints or Animate.css, jQuery allows us to easily trigger our animations.

1. Using Waypoints

The basic Waypoint function in jQuery looks like this:

$("#element-to-animate").waypoint(function() {
    // animation code
}, { offset: '100%'});

The 'offset' value determines at what point the animation is triggered. By default, the animation is triggered when the element you are animating reaches the top of the viewport.

The offset can be set as a percentage of the viewport (eg, setting 'offset: 30%' triggers the animation when the element is 30% from the top of the window), or as a value that translates to pixels (setting 'offset: 100' triggers the animation when the element is 100px from the top of the window).

Generally, we’ve found that having the animation trigger lower on the screen works best. This would translate into a larger offset value.

2. Combining with Animate.css

To create the actual animation, we use Animate.css. Animate.css allows us to create sophisticated animations solely through CSS classes—we simply add the 'animated' class and the animation name (we use 'fadeInRight', 'fadeInLeft', and 'fadeInUp') to the target element.

When paired with Waypoints, we can wait until the user reaches a certain point on the screen before adding the actual animation name, thereby firing the animation at the exact desired moment:

$("#element-to-animate").waypoint(function() {
}, { offset: '100%'});

3. Tying it All Together

Finally, we’ll need to hide the element we wish to animate in order to create for the “reveal” effect that we want to achieve. Fortunately, Animate.css gives us the handy 'fade' class to do this. Update: We can easily hide the element by setting its opacity to zero with CSS.

The 'fade' class sets the 'opacity' of the element to zero, thereby hiding it but not taking it out of the page structure. This is important, as we don’t want the page to reflow when our element is revealed.

Here it is all together:


  // hide our element on page load
  $('#element-to-animate').css('opacity', 0);

  $('#element-to-animate').waypoint(function() {
  }, { offset: '50%' });


To see Waypoints and Animate.css in action, head over to the Atomic Object homepage and scroll down!

  • Antonio says:


  • Chris says:

    The hiding of the element is not working for me

    • DigiBeaver says:

      I encountered that too, after looking closely there’s some characters missing at the end of Bobby’s codes. It should be:

      // hide our element on page load
      $(‘#element-to-animate’).css(‘opacity’, ‘0’);

      $(‘#element-to-animate’).waypoint(function() {
      }, { offset: ‘50%’ });

      }); // notice I’ve added the ); here, compared to the original code

      This now works for me, hope it’ll work for you too.

      • Bobby Kilpatrick Bobby Kilpatrick says:

        Thanks for noticing this. I corrected the code in the post to reflect this.

  • Joseph Rex says:

    It’s either it’s an old version of animateCSS that allows you used the .fade class or you have some other framework or utility class in your codebase that makes .fade available but it doesn’t work. I’m using the vanilla JS implementation of this but you gave me the pointers. Thanks

  • jeff says:

    Is it possible to reverse the animation? So that when you scroll up, everything zooms back out, then when you scroll back down, everything comes out?

    • akshay says:

      ya its possible you can remove the class when scroll up give waypoint parameter like this
      if(direction == ‘Down’){

  • garlacour says:

    Its not work on my mobile :/ is it normal ?

  • Entropy says:

    This is great, thank you!

    Is there any way to prevent the animations firing in again if the user refreshes when a little further down the page?

    For example, the Atomic Object home page seems to prevent this from happening – which I think is a great bit of usability.


  • Adam says:

    $(“#element-to-animate”).waypoint(function() {
    }, { offset: ‘100%’});

    Missing the closing string in the second ‘#element-to-animate’ reference

  • Manny Sangha says:

    I can’t seem to get this working… here is my code…any help will be greatly appreciated…


    Title of the document


    // hide our element on page load
    $(‘#element-to-animate’).css(‘opacity’, 0);

    $(‘#element-to-animate’).waypoint(function() {
    }, { offset: ‘50%’ });


    This is the content for Layout P Tag


  • Sibichan says:

    How to calculate offset value?

  • Arjun Kapoor says:

    I used the above explained concept on my website, it works fine but when you load your website ie when js and css are not in browser cache it does not work. Even in the sample given you can see the issue. Can you please tell a work around for it? It would be really helpful

  • Noor says:

    Vertical (basic) form


    // hide our element on page load
    $(‘#element-to-animate’).css(‘opacity’, 0);

    $(‘#element-to-animate’).waypoint(function() {
    }, { offset: ‘50%’ });


    Why not working? dear

  • Sajjad says:

    Hi Booby, great and helpful tutorial. I am using this , I think this is also simple
    $(document).ready(function(e) {
    var a = 400,
    t = 1200,
    l = 700,
    s = e(“.scrool-top”);
    e(window).scroll(function() {
    e(this).scrollTop() > a ? s.addClass(“scrool-is-visible”) : s.removeClass(“scrool-is-visible scrool-fade-out”), e(this).scrollTop() > t && s.addClass(“scrool-fade-out”)
    }), s.on(“click”, function(a) {
    a.preventDefault(), e(“body,html”).animate({
    scrollTop: 0
    }, l)

  • Sourav says:

    Your code has errors.

  • Hooman says:

    Hi and thanks

  • sopha says:

    Hi your code is not work.

  • ray theme says:

    Thank you for your good info.

  • Kevin says:

    Thank you!

  • Erick says:


    I tried to hide the element, but somehow the animation doesnt come in at all. So all i get is a blank element. I’m using squarespace. anyone have the same issue?


  • Brandon says:

    I’ve been struggling with different javascript “scroll into view” methods for days on end trying to get something to work with my canvas animation with absolutely no luck. I finally discovered your page, plugged in the tiny bit of code and – boom – worked flawlessly. Thank you so much for this simple and effective method!

  • Natacha says:

    This was awesome!!! Thank you so much for posting this. I tried using waypoints and jquery a few months ago to do something similar but couldn’t get the result I wanted so gave it up. Using animate.css is the way to go!

    • Natacha says:

      If anyone was getting your elements flashing on page load, I added the “animated” class dynamically with jQuery (instead of right in the HTML) and that appeared to fix it. For example:


      jQuery(‘#workimg’).waypoint(function() {
      }, { offset: ‘50%’ });

  • Alessandro says:

    i can’t understand my mistake.. anyone help?

    @keyframes fadeInRight {
    from {
    opacity: 0;
    transform: translate3d(100%, 0, 0);

    to {
    opacity: 1;
    transform: none;

    .fadeInRight {
    animation-name: fadeInRight;




    // hide our element on page load
    $(‘#element-to-animate’).css(‘opacity’, 0);

    $(‘#element-to-animate’).waypoint(function() {
    }, { offset: ‘50%’ });


  • Comments are closed.