Periodically animate a number using Javascript
I have an element on my page that I would like to increase from time to time:
<p class="count">28</p> In my javascript, I have an interval that runs once every 5 seconds and asynchronously captures a new (higher) number from the server. This works somewhat along the lines of the counter.
setInterval(function(){ $.post("getcount.php", function(num){ latestNum = num; }); }, 5000); If an asynchronous request reduces the number 56, I would like the paragraph text to change from 28 to 56, showing many (or most) or intermediate elements while it grows. What I'm doing right now sets a different spacing to constantly check the paragraph text and local variable. If the text of the paragraph is lower than the value of the variable, it increments it by 1. It does this every half second.
setInterval(function(){ currNum = Number( $(".count").text() ); if (currNum < latestNum) $(".count").text( currNum + 1 ); }, 50); My question is this: is there a better way to do this without a constant break? Is this an animated increment something that I can call from the response of an asynchronous request and then stop it as soon as these two numbers match the value? I should also note that there is a possibility that the next query will take place before the two numbers are matched by value.
How would you do this in a team?
Here my answer is actually two answers.
Moving linearly to the target value with a fixed interval risks that it will never get there. Therefore, I also gave another solution that reduces the difference every time, it becomes much faster even for large differences.
Both examples can also count.
$( function() { var tRef, counter = $( '#counter' ); target = $( '#target' ); /* * A function that eases towards its target */ function convergeEasing( target ) { clearTimeout( tRef ); target = parseInt( target ); var current = parseInt( counter.html() ); var diff = target - current; if( diff ) { var rounder = diff > 0 ? Math.ceil : Math.floor; current += rounder( diff/2 ); counter.html( current ); tRef = setTimeout( function() { convergeEasing( target ); }, 250 ); } } /* * A function that plods towards its target */ function convergeLinear( target ) { clearTimeout( tRef ); target = parseInt( target ); var current = parseInt( counter.html() ); var diff = target - current; if( diff ) { current += diff > 0 ? 1 : -1; counter.html( current ); tRef = setTimeout( function() { convergeLinear( target ); }, 250 ); } } /* * I've mocked your ajax request up here for demo purposes * using the linear way as per your question */ setInterval( function(){ var n = Math.round( Math.random()*1000 ); target.html( n ); convergeLinear( n ); }, 5000 ); }); <div id="target">20</div> <div id="counter">20</div> I believe that the easing strategy may be a function that is passed in place of two functions that duplicate most of their code
Corrected answer:
Commentators: realized that I ... still thank you!
var interval = setInterval(function(){ currNum = Number( $(".count").text() ); if (currNum < latestNum) { $(".count").text( currNum + 1 ); } else { setTimeOut(function() { clearInterval(interval) }, 0); } }, 50); This saves the interval in a variable, and then calls clearInterval to stop the interval. setTimeOut is necessary, since otherwise the interval will be cleared when it is executed.
First answer:
Why not combine two functions, that is:
setInterval(function(){ $.post("getcount.php", function(num){ latestNum = num; if (currNum < latestNum) { currNum = latestNum; $(".count").text( currNum ); } }); }, 5000); This will prevent updates every half second.
Based on Obalix's answer, I would recommend that the 5 second interval trigger a smaller interval.
var interval = null, currNum = null; setInterval(function(){ $.post("getcount.php", function(num){ latestNum = num; if (currNum === null) currNum = Number( $(".count").text() ); // should only execute once if (currNum < latestNum && interval === null) interval = setInterval(ObalixFunction, 50); }); }, 5000); and change
function ObalixFunction() { if (currNum < latestNum) { // increment the currNum variable too so we don't have to keep parsing it $(".count").text( ++currNum ); } else { setTimeOut(function() { clearInterval(interval); interval = null; }, 0); } }