Kyan
Kyan

Reputation: 55

jQuery counter, counting down after reaching the number

I'm using a jQuery code for showing animated stats for my web page.

The number animates successfully, but a few seconds after reaching its max value it starts counting down back to 1! How can I fix this?

// Checks if any particular element is 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 elementTop < viewportBottom;
};

// Animate numbers when scrolled into view
$(window).scroll(function() {
  var counters_triggered = false;
  var $counter_animated = $('.stat-counters > .stat > .counter > span');

  if ($('.stat-counters').isInViewport() && !counters_triggered) {
    counters_triggered = true;

    $.each($counter_animated, function() {
      var $counter = $(this);
      $counter.prop('Counter', 0).animate({
        Counter: $counter.text()
      }, {
        duration: 5000,
        easing: 'swing',
        step: function(now) {
          $counter.text(Math.ceil(now));
        }
      });
    });
  }
});
.padding { height: 1000px; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Scroll down...
<div class="padding"></div>
<div class="stat-counters d-flex justify-content-between align-items-start">
  <div class="stat col-md-4 px-0">
    <div class="counter">
      <span>10</span>+
    </div>
    <span class="text">Years of Experience</span>
  </div>
</div>

Upvotes: 1

Views: 1102

Answers (1)

Rory McCrossan
Rory McCrossan

Reputation: 337560

The issue is because your logic to prevent the animation being triggered multiple times on scroll isn't quite correct, and is in fact queuing the event handler multiple times.

Your isInViewport() function needs to be called on each individual element, not the collection. As such the if condition should be inside the $.each().

In addition you're using a single variable to manage state of all of the animatable elements. You can manage their state individually by using a data attribute on them individually.

With that said, try this:

// Checks if any particular element is 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 elementTop < viewportBottom;
};

// Animate numbers when scrolled into view
$(window).scroll(function() {
  $('.stat-counters > .stat > .counter > span').each((i, el) => {
    var $counter = $(el);
    if (!$counter.isInViewport() || $counter.data('animation-started'))
      return;

    $counter.data('animation-started', true).prop('Counter', 0).animate({
      Counter: $counter.text()
    }, {
      duration: 5000,
      easing: 'swing',
      step: function(now) {
        $counter.text(Math.ceil(now));
      }
    });
  });
});
.padding {
  height: 1000px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Scroll down...
<div class="padding"></div>
<div class="stat-counters d-flex justify-content-between align-items-start">
  <div class="stat col-md-4 px-0">
    <div class="counter">
      <span>10</span>+
    </div>
    <span class="text">Years of Experience</span>
  </div>
</div>

Upvotes: 1

Related Questions