0

I am trying to increment from 0 to a number (can be any number from 2000 to 12345600000) within a certain duration (1000 ms, 5000 ms, etc). I have created the following:

http://jsfiddle.net/fmpeyton/c9u2sky8/

var counterElement = $(".lg-number");
var counterTotal = parseInt(counterElement.text()/*.replace(/,/g, "")*/);
var duration = 1000;
var animationInterval = duration/counterTotal;

counterElement.text("0");
var numberIncrementer = setInterval(function(){
    var currentCounterNumber = parseInt(counterElement.text()/*.replace(/,/g, "")*/);
    
    if (currentCounterNumber < counterTotal){
        currentCounterNumber += Math.ceil(counterTotal/duration);
    
        // convert number back to comma format
        // currentCounterNumber = addCommas(currentCounterNumber);
        
        counterElement.text(currentCounterNumber);
    } else {
        counterElement.text(counterTotal);
        clearInterval(numberIncrementer);
    }
    
    console.log("run incrementer");
}, animationInterval);

function addCommas(number){
    for (var i = number.length - 3; i > 0; i -= 3)
        number = number.slice(0, i) + ',' + number.slice(i);
    
    return number;
}

And this somewhat works, but it does not respect the duration. I.e. if you increase the number from 1000 to 1000000000, they both take different amounts of time to reach the destination number.

How can I increment from zero to a number in a specific time frame?

Community
  • 1
  • 1
Fillip Peyton
  • 3,637
  • 2
  • 32
  • 60
  • http://stackoverflow.com/questions/1379934/large-numbers-erroneously-rounded-in-javascript, please read this regarding very large numbers. – Mouser Mar 11 '15 at 18:19
  • 2
    This will be due to the fact that you do a division `duration/counterTotal` here that will be rounded down or up, also the duration can't go lower than 1 millisecond. – Mouser Mar 11 '15 at 18:25
  • Thanks for the comments @Mouser. Even within the limits of Javascript numbers (9000 million million), I still see this error. – Fillip Peyton Mar 11 '15 at 18:27
  • I guess that you will start to see differences when the duration comes above 1 millisecond. – Mouser Mar 11 '15 at 18:28
  • That makes sense. Is there any way I can achieve this? – Fillip Peyton Mar 11 '15 at 18:30

2 Answers2

1

As @Mouser pointed out, the issue is that the animationInterval can't be too small (the actual minimum threshold will vary based on the browser and platform). Instead of varying the interval, vary the increment to the counter:

var counterElement = $(".lg-number");
var counterTotal = parseInt(counterElement.text()/*.replace(/,/g, "")*/);
var duration = 1000;
var animationInterval = 10;
var startTime = Date.now();

counterElement.text("0");
var numberIncrementer = setInterval(function(){
    var elapsed = Date.now() - startTime;
    var currentCounterNumber = Math.ceil(elapsed / duration * counterTotal);
    if (currentCounterNumber < counterTotal){
        counterElement.text(currentCounterNumber);
    } else {
        counterElement.text(counterTotal);
        clearInterval(numberIncrementer);
    }

    console.log("run incrementer");
}, animationInterval);
radiaph
  • 4,001
  • 1
  • 16
  • 19
1

I played around with your fiddle and found that the delay needs to be higher. At 8ms or 16ms, it is accurate enough to handle a second, but not accurate enough to handle half a second. From experimenting, it seems like a delay of 64ms is small enough to seem like it's incrementing smoothly, but big enough to have an accurate effect.

The difference is that the current number is calculated based on the process rather than directly manipulated.

var counterElement = $(".lg-number");
var counterTotal = parseInt(counterElement.data('total'));
var interval = 0;
var duration = parseInt(counterElement.data('duration'));;
var delay = 64

var numberIncrementer = setInterval(function(){
    var currentCounterNumber = 0;

    interval += delay;

    if (interval <= duration){
        var progress = interval / duration;
        currentCounterNumber = Math.round(progress * counterTotal);
    } else {
        currentCounterNumber = counterTotal
        clearInterval(numberIncrementer);
    }

    counterElement.text(currentCounterNumber);
}, delay);

http://jsfiddle.net/c9u2sky8/5/

Also: Javascript timers are not perfectly accurate. But this should be accurate enough for UI use cases.

Community
  • 1
  • 1
Desmond Lee
  • 707
  • 6
  • 17