Search code examples
javascriptjqueryanimationcss-animationsviewport

How to fix double animation effect when using $(window).scroll function?


I'm trying to fix an issue whereas when the user scrolls, the elements in viewport already, starts triggering the animation a second time. This only happens when the page is loaded at the very top of the page then proceeding to scroll down. When positioned anywhere else on the page, it works fine and elements animate correctly and doesn't trigger twice in-view. To reiterate, the main issue is when the <section> tags animate, it'll animate again after page load when I scroll from the very top down, sort like a flashback initially, which I don't want. How do I fix this bug?

Here's the jsfiddle: https://jsfiddle.net/TheAmazingKnight/oybta7dp/10/

Code Attempt: I tried something like this to do a check if these elements are already in view, dont trigger the animation until it has left view and reenters, but it doesn't work.

if ($(this).isInViewport()) {
 if($(this).parent().hasClass("animate-section") && $(this).parent().hasClass("animate")) {
   $(this).removeClass('animate-element');
   $(this).removeClass('animate');
   return false; // abort function if element is already in view preventing double animation
 }
}

jQuery Code:

$(document).ready(function() {

  var flag = true; // var to detect if function already ran once

  if (flag) {
    // Set the animation to be triggered when the section comes into view
    var sections = $('.animate-section');
    sections.each(function() { // check which sections are in-view on page load
      if ($(this).isInViewport()) {
        $(this).addClass('animate'); // Add the "animate" class to trigger the animation
      } else {
        $(this).removeClass('animate'); // Remove the "animate" class if the section is out of view
      }
    });
    flag = false; // change boolean var as function already ran once
  }
  // Set the animation to be triggered when the element comes into view
  var elements = $('.animate-element');
  $(window).scroll(function() {
    elements.each(function() {
      if ($(this).isInViewport()) {
            if($(this).parent().hasClass("animate-section") && $(this).parent().hasClass("animate")) {
                $(this).removeClass('animate-element');
                $(this).removeClass('animate');
                //return false; // abort function if element is already in view preventing double animation
            }
        $(this).css("visibility", "visible");
        $(this).addClass('animate'); // Add the "animate" class to trigger the animation
      } else {
        $(this).css("visibility", "hidden");
        $(this).removeClass('animate'); // Remove the "animate" class if the section is out of view
      }
    });
  });

});
// jQuery function to check if element is visible in viewport
$.fn.isInViewport = function() {
  var elementTop = $(this).offset().top;
  var elementBottom = elementTop + $(this).outerHeight();

  var viewportTop = $(window).scrollTop();
  var viewportBottom = viewportTop + $(window).height();

  return elementBottom > viewportTop && elementTop < viewportBottom;
};

EDIT:

Since this double animation effect only occurs at the top of the page, I added a code hack workaround for the time being. But if anyone can figure out a resolve would be great. Thanks!

$(document).ready(function () {
  // code hack to scroll ever-so-slightly to trigger animating the elements only if initially loaded at the top of page to avoid double animation effect when user scrolls  
  if (window.scrollY == 0) {
    Scrolldown();
  }
});

// function to scroll ever-so-slightly
function Scrolldown() {
   window.scroll(0, 1);
}

Solution

  • If I got it correctly, you want the animation for all the elements in the view when the page load, and then, just for the new ones while scrolling. I played around with your code and this is what I came up with.

    /* CHAT PROMPT: Add some jQuery code where as when the user scrolls through the page, the section will animate as they come into view. */
    $(document).ready(function() {
    
     $('.animate-element').addClass("animate");
      
      // Set the animation to be triggered when the element comes into view
      var elements = $('.animate-element');
      $(window).scroll(function() {
        elements.each(function() {
          if ($(this).isInViewport()) {              
            $(this).css("visibility", "visible");
            $(this).addClass('animate'); // Add the "animate" class to trigger the animation
          } else {
            $(this).css("visibility", "hidden");
            $(this).removeClass('animate'); // Remove the "animate" class if the section is out of view
          }
        });
      });
    
    });
    // jQuery function to check if element is visible in viewport
    $.fn.isInViewport = function() {
      var elementTop = $(this).offset().top;
      var elementBottom = elementTop + $(this).outerHeight();
    
      var viewportTop = $(window).scrollTop();
      var viewportBottom = viewportTop + $(window).height();
    
      return elementBottom > viewportTop && elementTop < viewportBottom;
    };
    

    I just removed that flag logic and added the "animate" class to all the element that should be animated at loading. When the scroll function is triggered, those elements already have the "animate" class, so they won't be animated again.

    Here the jfiddle: https://jsfiddle.net/r25v7qot/1/