jqueryanimationjquery-animatesettimeoutonscroll

Animating Elements in One by One


I am trying to make an on scroll jQuery sequence that animates elements in one after the other when it's in view. Here is my CodePen and here is the code:

$(document).ready( function() {
    
  var $window = $(window);
  var $animatedItem = $('.has-animation');
  $animatedItem.css({ 
    "visibility": "hidden"
  });

  // Check if in view.
  function runAnimation() {
    var windowTop = $window.scrollTop();
    var windowHeight = $window.height();
    var windowWidth = $window.width();
    var windowBottom = (windowTop + windowHeight + windowWidth);

    $animatedItem.each( function() {
      var $element = $(this);
      var elementTop = $element.offset().top;
      var elementHeight = $element.outerHeight();
      var elementWidth = $element.outerWidth();
      var elementBottom = (elementTop + elementHeight + elementWidth);
      
      if (windowTop > elementTop - windowHeight / 1.2) {      
        // Check to see if this current container is within viewport.
        if ((elementBottom >= windowTop) && (elementTop <= windowBottom)) {
          
          $element.each( function(i) {
            // Stagger the elements into view.     
            setTimeout( function() {
              $element.eq(i).removeClass('has-animation').css({
                "visibility": "visible"
              });
              $element.eq(i).addClass('animated');
              $element.eq(i).addClass('in-view');
            }, 330 * (i+1));
            
          });

        }
      }
    });
  }
$window.on('scroll resize', runAnimation);
$window.trigger('scroll');

});

I have it so the class ".has-animation" is replaced with ".animated" and ".in-view" when the element is scrolled into view. The element style "visibility" is also changed from hidden to visible.

Currently, when the elements reach the viewpoint, they all simultaneously animate in instead of each element animating in one after another.

I have tried replacing $element in this code:

$element.each( function(i) {
  // Stagger the elements into view.     
  setTimeout( function() {
    $element.eq(i).removeClass('has-animation').css({
      "visibility": "visible"
    });
      $element.eq(i).addClass('animated');
      $element.eq(i).addClass('in-view');
    }, 330 * (i+1));
        
});

With $animatedItem so it looks like this:

$animatedItem.each( function(i) {
  // Stagger the elements into view.     
  setTimeout( function() {
    $animatedItem.eq(i).removeClass('has-animation').css({
      "visibility": "visible"
    });
      $animatedItem.eq(i).addClass('animated');
      $animatedItem.eq(i).addClass('in-view');
    }, 330 * (i+1));
        
});

And this code (the $animatedItem one) actually does what I'm looking for, however, the elements animate immediately upon scroll rather than loading when they get to the viewpoint. This leads me to believe that a function like this should be directed to $animatedItem but near the start of my code?

I'm new (self learning) to JS/JQuery, and I'm having a bit of trouble figuring this out, any help is appreciated! Thanks all!


Solution

  • The key is about timing.

    As soon as some elements get in-view, remove the has-animation class... THEN, set the timeout for the CSS animation to trigger.

    Also, you have to refresh the $animatedItem collection... ;)

    The result of this answer is best viewed in CodePen

    $(document).ready(function () {
      var $window = $(window);
      var $animatedItem = $(".has-animation");
      $animatedItem.css({
        visibility: "hidden"
      });
    
      // Check if in view.
      function runAnimation() {
        var windowTop = $window.scrollTop();
        var windowHeight = $window.height();
        var windowWidth = $window.width();
        var windowBottom = windowTop + windowHeight + windowWidth;
    
        $animatedItem.each(function (animated_index) {
          var $element = $(this);
          var elementTop = $element.offset().top;
          var elementHeight = $element.outerHeight();
          var elementWidth = $element.outerWidth();
          var elementBottom = elementTop + elementHeight + elementWidth;
    
          // Refresh the collection
          $animatedItem = $(".has-animation");
    
          if (windowTop > elementTop - windowHeight / 1.2) {
            // Check to see if this current container is within viewport.
            if (elementBottom >= windowTop && elementTop <= windowBottom) {
              $element.each(function (i, el) {
                // Stagger the elements into view.
                if ($(el).hasClass("has-animation")) {
                  $(el).eq(i).removeClass("has-animation");
    
                  setTimeout(function () {
                    console.log(animated_index);
                    $(el).css({
                      visibility: "visible"
                    });
                    $(el).addClass("animated in-view");
                  }, 330 * (animated_index + 1));
                }
              });
            }
          }
        });
      }
      $window.on("scroll resize", runAnimation);
      $window.trigger("scroll");
    });
    /*-----------------------------------------*/
    /* Content
    /*-----------------------------------------*/
    
    body {
      background-color: #EBE7DE;
    }
    
    .content-block {
      margin: 0 auto!important;
      width: 100%;
      clear: both;
    }
    
    .common-block {
      display: block;
      position: relative;
      width: 100%;
      margin: 0 auto!important;
    }
    
    .content-title {
      font-style: italic;
      color: #19130E;
      margin-top: 40px;
      margin-left: 40px;
    }
    
    .content-spacer {
      height: 1400px;
    }
    
    body .relative { 
      position: relative; 
    }
    
    body .width-1of1 { 
      width: 100%; }
    
    body .white-bg { 
      background-color: #FFF;
    }
    
    
    /*-----------------------------------------*/
    /* Column Group
    /*-----------------------------------------*/
    
    .column-group {
      display: flex;
      flex-flow: row wrap;
      justify-content: center;
      margin-bottom: 0px;
      padding: 0;
    }
    
    .column {
      display: flex;
      position: relative;
      list-style: none;
      vertical-align: top;
      margin: 0px;
      margin-bottom: 40px;
      padding-right: 20px;
      padding-left: 20px;
      background-clip: content-box;
    }
    
    .padding-bottom-1of1 {
      height: 0!important;
      padding-bottom: 100%!important;
    }
    
    .overflow-hidden {
      overflow: hidden!important;
    }
    
    body .absolute-fill {
      position: absolute;
      width: 100%;
      height: 100%;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
    }
    
    body .prepped-background {
      background-size: cover!important;
      background-repeat: no-repeat!important;
      background-position: center center!important;
    }
    
    body .opacity-0 {
      opacity: 0;
    }
    
    
    /*-----------------------------------------*/
    /* Devices
    /*-----------------------------------------*/
    
    /* DESKTOP */
    @media (min-width: 1021px) {
      
      .column-group {
        padding: 0rem 10rem; }
        
        html .column-group.desktop-width-1of3 > .column.responsive { 
        width: 33.3%; 
        box-sizing: border-box; }
      
      html body .desktop-max-width-450 {
        max-width: 450px; }
      
    }
    
    
    /* TABLET */
    @media (min-width: 768px) and (max-width: 1020px) {
      
      .column-group {
        padding: 0rem 5rem; }
    
        html .column-group.tablet-width-1of2 > .column.responsive { 
        width: 50%; 
        box-sizing: border-box; }
      
      html body .tablet-max-width-450 {
        max-width: 450px; }
      
    }
    
    
    /* MOBILE */
    @media (max-width: 767px) {  
      
      .column-group {
        padding: 0rem 4rem; }
      
      html .column-group.mobile-width-1of1 > .column.responsive { 
        width: 100%; 
        box-sizing: border-box; }
      
      html body .mobile-max-width-250 {
        max-width: 250px; }
      
      body .mobile-margin-x-auto {
        margin: 0 auto!important;  }
      
    }
    
    /*-----------------------------------------*/
    /* Animations
    /*-----------------------------------------*/
    
    .has-animation {  
        -webkit-animation-play-state: paused;   
        -moz-animation-play-state: paused;
        -ms-animation-play-state: paused;
        -o-animation-play-state: paused;
        animation-play-state: paused;
    }
    
    .animated[class*="in-view"] {
        -webkit-animation-fill-mode: both;
        -moz-animation-fill-mode: both;
        -ms-animation-fill-mode: both;
        -o-animation-fill-mode: both;
        animation-fill-mode: both;
        
        
    }
    
    .animated {
        -webkit-animation-fill-mode: both;
        -moz-animation-fill-mode: both;
        -ms-animation-fill-mode: both;
        -o-animation-fill-mode: both;
        animation-fill-mode: both;
        
        -webkit-animation-duration: 1s;
        -moz-animation-duration: 1s;
        -ms-animation-duration: 1s;
        -o-animation-duration: 1s;
        animation-duration: 1s;
    
        -webkit-animation-play-state: running;
        -moz-animation-play-state: running;
        -ms-animation-play-state: running;
        -o-animation-play-state: running;
        animation-play-state: running;
    }
    
    .animated[class*="in-view"].fade-in-up {
        animation-name: fade-in-up;
        -webkit-animation-name: fade-in-up;
        -moz-animation-name: fade-in-up;
        -o-animation-name: fade-in-up;
    
        -webkit-animation-delay: .5s;
        -moz-animation-delay: .5s;
        -ms-animation-delay: .5s;
        -o-animation-delay: .5s;
        animation-delay: .5s;
    
        -webkit-animation-duration: 1s;
        -moz-animation-duration: 1s;
        -ms-animation-duration: 1s;
        -o-animation-duration: 1s;
        animation-duration: 1s;
    }
    
    @keyframes fade-in-up {
        0% { 
          opacity: 0; 
          transform: translateY(20px); } 
        100% { 
          opacity: 1; 
          transform: translateY(0); }
    }
    
    @-webkit-keyframes fade-in-up {
        0% { 
          opacity: 0; 
          -webkit-transform: translateY(20px); }
        100% { 
          opacity: 1; 
          -webkit-transform: translateY(0); }
    }
    
    @-moz-keyframes fade-in-up {
        0% { 
          opacity: 0; 
          -moz-transform: translateY(20px); }
        100% { 
          opacity: 1; 
          -moz-transform: translateY(0); }
    }
    
    @-o-keyframes fade-in-up {
        0% { 
          opacity: 0; 
          -o-transform: translateY(20px); }
        100% { 
          opacity: 1; 
          -o-transform: translateY(0); }
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <div class="content-block">  
      <div class="common-block">
        <h1 class="content-title">Scroll Down to Trigger the Animations...</h1>
        <div class="content-spacer"></div>    
        <ul class="column-group desktop-width-1of3 tablet-width-1of2 mobile-width-1of1">
          <li class="column responsive has-animation fade-in-up">
            <div class="relative width-1of1">
                        <div class="relative white-bg padding-bottom-1of1 width-1of1 overflow-hidden">
                          <div class="absolute-fill">
                                <div class="absolute-fill prepped-background" data-src="https://www.tompetty.com/sites/g/files/g2000007521/f/sample_01.jpg" style="background-image: url('https://www.tompetty.com/sites/g/files/g2000007521/f/sample_01.jpg');"></div><img class="absolute-fill opacity-0" data-src="https://www.tompetty.com/sites/g/files/g2000007521/f/sample_01.jpg" src="https://www.tompetty.com/sites/g/files/g2000007521/f/sample_01.jpg" srcset="https://www.tompetty.com/sites/g/files/g2000007521/f/sample_01.jpg">
                                 </div>
                            </div>
                    </div>
          </li>
          
          <li class="column responsive has-animation fade-in-up">
            <div class="relative width-1of1">
                        <div class="relative white-bg padding-bottom-1of1 width-1of1 overflow-hidden">
                          <div class="absolute-fill">
                                <div class="absolute-fill prepped-background" data-src="https://www.tompetty.com/sites/g/files/g2000007521/f/sample_01.jpg" style="background-image: url('https://www.tompetty.com/sites/g/files/g2000007521/f/sample_01.jpg');"></div><img class="absolute-fill opacity-0" data-src="https://www.tompetty.com/sites/g/files/g2000007521/f/sample_01.jpg" src="https://www.tompetty.com/sites/g/files/g2000007521/f/sample_01.jpg" srcset="https://www.tompetty.com/sites/g/files/g2000007521/f/sample_01.jpg">
                                 </div>
                            </div>
                    </div>
          </li>
          
          <li class="column responsive has-animation fade-in-up">
            <div class="relative width-1of1">
                        <div class="relative white-bg padding-bottom-1of1 width-1of1 overflow-hidden">
                          <div class="absolute-fill">
                                <div class="absolute-fill prepped-background" data-src="https://www.tompetty.com/sites/g/files/g2000007521/f/sample_01.jpg" style="background-image: url('https://www.tompetty.com/sites/g/files/g2000007521/f/sample_01.jpg');"></div><img class="absolute-fill opacity-0" data-src="https://www.tompetty.com/sites/g/files/g2000007521/f/sample_01.jpg" src="https://www.tompetty.com/sites/g/files/g2000007521/f/sample_01.jpg" srcset="https://www.tompetty.com/sites/g/files/g2000007521/f/sample_01.jpg">
                                 </div>
                            </div>
                    </div>
          </li>
          
          <li class="column responsive has-animation fade-in-up">
            <div class="relative width-1of1">
                        <div class="relative white-bg padding-bottom-1of1 width-1of1 overflow-hidden">
                          <div class="absolute-fill">
                                <div class="absolute-fill prepped-background" data-src="https://www.tompetty.com/sites/g/files/g2000007521/f/sample_01.jpg" style="background-image: url('https://www.tompetty.com/sites/g/files/g2000007521/f/sample_01.jpg');"></div><img class="absolute-fill opacity-0" data-src="https://www.tompetty.com/sites/g/files/g2000007521/f/sample_01.jpg" src="https://www.tompetty.com/sites/g/files/g2000007521/f/sample_01.jpg" srcset="https://www.tompetty.com/sites/g/files/g2000007521/f/sample_01.jpg">
                                 </div>
                            </div>
                    </div>
          </li>
          
          <li class="column responsive has-animation fade-in-up">
            <div class="relative width-1of1">
                        <div class="relative white-bg padding-bottom-1of1 width-1of1 overflow-hidden">
                          <div class="absolute-fill">
                                <div class="absolute-fill prepped-background" data-src="https://www.tompetty.com/sites/g/files/g2000007521/f/sample_01.jpg" style="background-image: url('https://www.tompetty.com/sites/g/files/g2000007521/f/sample_01.jpg');"></div><img class="absolute-fill opacity-0" data-src="https://www.tompetty.com/sites/g/files/g2000007521/f/sample_01.jpg" src="https://www.tompetty.com/sites/g/files/g2000007521/f/sample_01.jpg" srcset="https://www.tompetty.com/sites/g/files/g2000007521/f/sample_01.jpg">
                                 </div>
                            </div>
                    </div>
          </li>
          
          <li class="column responsive has-animation fade-in-up">
            <div class="relative width-1of1">
                        <div class="relative white-bg padding-bottom-1of1 width-1of1 overflow-hidden">
                          <div class="absolute-fill">
                                <div class="absolute-fill prepped-background" data-src="https://www.tompetty.com/sites/g/files/g2000007521/f/sample_01.jpg" style="background-image: url('https://www.tompetty.com/sites/g/files/g2000007521/f/sample_01.jpg');"></div><img class="absolute-fill opacity-0" data-src="https://www.tompetty.com/sites/g/files/g2000007521/f/sample_01.jpg" src="https://www.tompetty.com/sites/g/files/g2000007521/f/sample_01.jpg" srcset="https://www.tompetty.com/sites/g/files/g2000007521/f/sample_01.jpg">
                                 </div>
                            </div>
                    </div>
          </li>
          
        </ul>    
      </div>  
    </div>