Dgener8Snoogns
Dgener8Snoogns

Reputation: 33

Javascript: How To Count to A Specific Number?

So I was searching on StackOverflow today, and I found a couple items that do this in jquery, but I'm trying to accomplish this in plain vanilla JS. I'm kind of new to this, not brand new, but still learning. I'm trying to make it to where the count starts at zero (0) and counts to a number to be determined. I'm actually going to have it count 4 times on a page, but so far what I have is taking too long.

https://codepen.io/tony-blackford/pen/wvwYQqx

function animateValue(id, start, end, duration) {
  var range = end - start;
  var current = start;
  var increment = end > start? 1 : -1;
  var stepTime = Math.abs(Math.floor(duration / range));
  var obj = document.getElementById(id);
  var timer = setInterval(function() {
    current += increment;
    obj.innerHTML = current;
    if (current == end) {
      clearInterval(timer);
    }
  }, stepTime);
   }

animateValue('value', 0, 1004945, 3000);

This will do the count, I found the code online somewhere I don't recall, but given that the number is so large it is taking way too long. So I'm hoping someone can either help me with this code, or point me to a better option. I'm also trying to make it part of an event listener so it doesn't trigger until it scrolls into view. Any help is appreciated. Like I said I'm new at this.

Upvotes: 0

Views: 1277

Answers (3)

vol7ron
vol7ron

Reputation: 42109

Note: I like Jaromanda's answer better.

For the purpose of coverage, here might be one method to continue to use setInterval. It's important to note that there are a number of factors that effect the timing of browser paints. The method below takes almost none of them into effect. Instead, it attempts to calculate and adjust the incrementor based on the range and duration. It then uses a random 29.5 to compensate for other lags, but this would most likely be device dependent.

animateValue('value', 0, 1004945, 3000);

function animateValue(id, start, end, duration) {
  var range     = end - start;
  var current   = start;
  var increment = end > start? 1 : -1;
  increment = increment*Math.floor(range/duration)*29.5;
  var stepTime  = Math.abs(Math.floor(duration / range));
  var obj   = document.getElementById(id);
  
  var startTime = (new Date()).getTime();
  var timer = setInterval(function() {
    current += increment;
    obj.innerHTML = current;
    if (current >= end) {
      clearInterval(timer);
      obj.innerHTML = end;
      logDuration(startTime, (new Date()).getTime());
    }
  }, stepTime);
}

function logDuration(startTime,endTime){
  document.querySelector('#duration').textContent = endTime - startTime + 'ms'
}
<div id="value"></div>
<div id="duration"></div>

Upvotes: 0

Georgy
Georgy

Reputation: 2462

You wouldn't get an idea answer here, speed depends on many factors, including computation speed and actual numbers, but you might use this approach, were little change from your example.

function animateValue(id, start, end, duration) {
  var range = end - start;
  var current = start;
  var increment = end > start ? 1 : -1;
  // you had sort of a bug here, duration is smaller than range
  // so `duration / range < 0`, so rounding with
  // `Math.floor()` was giving 0 in the end
  var stepTime = Math.abs(duration / range);
  var incrementsPerIteration = 1;
  // 4 is for 4ms, time for the minimal `setInterval`
  // incrementsPerIteration - are the number of consecutive
  // additions per iteration 
  if (stepTime < 4) {
    incrementsPerIteration = Math.ceil(4 / stepTime);
  }
  var obj = document.getElementById(id);
  var timer = setInterval(function() {
    for (var i = 0; i < incrementsPerIteration; i++) {
      current += increment;
      obj.innerHTML = current;
      if (current == end) {
        clearInterval(timer);
        break;
      }
    }
  }, stepTime);
}

animateValue('value', 0, 1004945, 3000);

Upvotes: 0

Jaromanda X
Jaromanda X

Reputation: 1

the problem is that the step time is 0.002985ms ... setInterval doesn't run that fast! what you should do is use requestAnimationFrame, which triggers every 16ms, then calculate the number to be displayed in each "frame"

function animateValue(id, start, end, duration) {
    var range = end - start;
    var current = start;
    var obj = document.getElementById(id);
    var starttime;
    var fn = (ms) => {
        let progress = 0;
        if(starttime === undefined) {
            starttime = ms;
        } else {
            progress = ms - starttime;
            if (progress >= duration) {
                // the `+ ' ' + progress + 'ms';` is just to show the duration, wouldn't use that in final code
                current = end.toLocaleString()  + ' ' + progress + 'ms';
            } else {
                current = start + Math.floor(progress/duration * range);
            }
        }
        obj.innerHTML = current.toLocaleString();
        if (progress < duration) {
            requestAnimationFrame(fn);
        }
    };
    requestAnimationFrame(fn);
}
animateValue('value', 0, 1004945, 3000);
<div id="value"></div>

Upvotes: 4

Related Questions