My javascript counter doesnt work on low numbers why? - javascript

const counters = document.querySelectorAll(".counter");
const speed = 1000;
counters.forEach((counter) => {
const updateCounter = () => {
const target = +counter.getAttribute("data-target");
const count = +counter.innerText;
const increment = target / speed ;
if (count < target) {
counter.innerText = Math.floor(count + increment);
setTimeout(updateCounter, 1);
} else {
counter.innerText = target;
}
};
updateCounter();
});
This is my javascript codes. Its work perfect for numbers bigger than 1000. But i need to use with lower numbers like "1-2-3-4-5". I couldn't find any solution. Anyone can help me?
I tried to change speed = 1000 top speed = 1 and it worked. But its too fast for animation

It's a little ambiguous which effect you're going for without accompanying HTML/CSS, but I'll take a stab. I think you may want to change
const target = +counter.getAttribute("data-target");
const count = +counter.innerText;
const increment = target / speed ;
to
const target = parseInt(counter.getAttribute("data-target"));
const count = parseInt(counter.innerText);
const increment = Math.ceil(target / speed);
I think the problem is:
If target < speed, then you're going to end up with a number less than one.
Then you add that number to the running count, which starts at zero.
Then you take Math.floor of the result, which will be zero.
I suspect the function as presented runs a number of times equal to the target number, but it's always changing the number to zero.
See this codepen: https://codepen.io/RyanStreur/pen/PoaBOrX?editors=1010

Related

Count to numbers above a billion in a few seconds without browser freezing

I am trying to make a counter that counts to big numbers such as 6 billion (like google's random number generator) but the browser freezes.
var counter = document.querySelector("#blahblahblah");
var nb = 0;
var ne = 6_000_000_000;
for (;nb<=ne;nb++) {
counter.innerHTML = nb;
};
The best thing you can do is use requestAnimationFrame. This represents the fastest rate that you can update the DOM.
Use the callback to update your element text to the number proportional to the time allowed; 3 seconds (a few) from your question title
const runCounter = (num, timeout, el) => {
let start;
const step = (ts) => {
if (!start) {
start = ts; // record the first iteration timestamp
}
const progress = (ts - start) / timeout; // how far into the time span
if (progress < 1) {
el.textContent = Math.floor(num * progress);
requestAnimationFrame(step); // request the next frame
} else {
el.textContent = num; // finish
}
};
requestAnimationFrame(step);
};
runCounter(6_000_000_000, 3000, document.getElementById("blahblahblah"));
<output id="blahblahblah">0</output>

Javascript | Floating point numbers + interval loop

I have a floating point number (for example): 0.004178174922295
How could I get the decremental function to make a calculations from this number to 0 in specific amount of time (for example 1 second)?
Thanks.
Expected values:
0.004178174922295
0.004178174922294
0.004178174922293
...
0
As raina77ow and others pointed out in the comments, operations with decimal numbers are problematic in JS, approximations are made and the results may be inexact.
A workaround would be turn the infinitesimals into big integral numbers, work with them and convert them back at the end.
Is this what you were looking for? Please let me know.
EDIT
You can ask for the countdown to be done in a certain amount of time, it does work with reasonable numbers, but in Javascript the minimum interval is of 10 milliseconds, you can't call intervals shorter than that. With the example number you gave, 0.004178174922295, it's like counting down from 4178174922295 to zero. That would requiere almost 1325 years in 10 millisecond intervals (if my math is correct, either way I expect you were going to pass a much shorter lapse).
function infinitesimalCountDown(num, seconds) {
// I create a coeficient to convert the decimal to an int
// Will be a big number starting with "1" and followed by a bunch of zeroes
let coefString = '1';
for(let i=0; i<num.toString().length-2; i++) {
coefString += '0';
}
const coef = Number(coefString);
// This has the digits from the original original but it's an int
let counter = Math.round(num*coef);
// Now I can just treat it as an int and convert it back for the output
const icdInterval = setInterval(() => {
counter--;
console.log(counter/coef);
if(counter <= 0) clearInterval(icdInterval);
}, Math.round(seconds*1000/counter));
}
console.log("It works with a short number");
infinitesimalCountDown(0.0041, 10);
setTimeout(() => {
console.log("It doesn't work with a long number");
infinitesimalCountDown(0.004178174922295, 3000);
}, 12 * 1000);
If you are fine with the steps being the necessary for Javascript to be able to process it, you can do the following:
function infinitesimalCountDown(num, seconds) {
let coefString = '1'
for(let i=0; i<num.toString().length-2; i++) {
coefString += '0'
}
const coef = Number(coefString)
let counter = Math.round(num*coef)
let steps = seconds * 1000 / counter
steps = steps < 100 ? 100 : steps
let step = 1
if(steps == 100) {
step = counter / ((seconds * 1000) / steps)
}
console.log(step)
const icdInterval = setInterval(() => {
counter -= step;
if(counter <= 0) {
counter = 0
clearInterval(icdInterval)
}
console.log(counter/coef)
}, steps)
}
infinitesimalCountDown(0.004178174922295, 5)
If you can represent the input number as number type (so not have many decimals), you can do this using normal number subtraction.
Here, the important thing is to get the unit to be subtracted. You can get the unit using Math.pow.
And from this floating point guide, it is needed to round the counted number and that can be done using toFixed function.
let input = 0.004178174922295;
const decimalCount = input.toString().length - 2;
const unit = Math.pow(10, -1 * decimalCount);
console.log(input);
const loopInterval = setInterval(() => {
input = Number((input - unit).toFixed(decimalCount));
console.log(input);
if (input == 0) {
clearInterval(loopInterval);
}
}, 1000);
And if the input number has many decimals so it is received as string type (not able to present using number type), it is needed to do the subtraction using string as follows.
const input = '0.0041781749222934534534534535';
const inputArr = input.split('.');
const intNum = inputArr[0]; // Present integer
let decimals = inputArr[1]; // Present decimals after '.'
const unit = 1;
function replaceAt(str, index, replace) {
return str.substring(0, index) + replace + str.substring(index + 1);
}
console.log(input);
const loopInterval = setInterval(() => {
let index = decimals.length - 1;
while (parseInt(decimals[index]) < unit) {
decimals = replaceAt(decimals, index --, '9');
}
decimals = replaceAt(decimals, index, `${parseInt(decimals[index]) - unit}`);
console.log(`${intNum}.${decimals}`);
}, 1000);

Increment from zero to number in a set time

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?
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);
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.

Javascript: Increasing/decreasing number to target

What is the best way to count from one preset number to a higher or lower number on input, and show the animation of the change?
There will be an initial amount, let say 100; and when we give it an increased or decreased number, let's say 200, the original 100 will count up animated to 200.
You can use setInterval like this:
var count = 100;
var number = 110;
var interval = setInterval(function(){
document.getElementById('elementID').innerHTML = ++count;
if (count === number) { clearInterval(interval) }
}, 500);
Use ++count if you want to increase or --count if you want to decrease.
Working Example
Not 100% sure I understand what you mean, but the count thing is simple:
function count() {
var i; //declare i for use later in the function
for (i = 100; i <= 200; i++) {
alert(i);
}
}

Incrementing a number smoothly with a variable time period in JS

I have a really simple JS counter which I display on a dashboard like screen which does the following:
Every 5 minutes it makes an jsonp call and retrieves a "total" number
It then displays this number to the screen by incrementing the last total displayed till it is equal to the new total. (the number can only ever increase)
I'm having some trouble with making the number increment smoothly. What I would like to do is find a delta (i.e. New total - old total) and increment the number gradually over the 5 minutes till the next call so it looks like a nice smooth transition.
Any ideas on how I can do this?
Currently some of my code looks like this (This block get's called every 5mins. And yes, it's in dire need of a refactor...)
var LAST_NUMBER_OF_SESSIONS = null;
var five_minutes_in_seconds = 300;
var new_number_of_sessions;
$.getJSON('http://blah.com/live_stats/default_jsonp.aspx?callback=?', function(data) {
if(LAST_NUMBER_OF_SESSIONS === null){
LAST_NUMBER_OF_SESSIONS = data.total_sessions;
}
new_number_of_sessions = data.total_sessions;
var delta = Math.floor(new_number_of_sessions - LAST_NUMBER_OF_SESSIONS);
var time_interval = (five_minutes_in_seconds / delta) * 1000;
var old_value = LAST_NUMBER_OF_SESSIONS;
var new_value = null;
sessions_interval = setInterval(function (){
new_value = parseInt(old_value, 10) + 1;
$('#stats').text(new_value);
old_value = new_value;
if(new_value >= new_number_of_sessions){
clearInterval(sessions_interval);
}
}, time_interval);
LAST_NUMBER_OF_SESSIONS = new_value;
});
}
This code it seems to increment the number very quickly at the start of the 5min period and then stop so it's not exactly right...
Try this:
var total = 0,
delta = 0,
stats = $('#stats').text( total );
function increment() {
var v = +stats.text();
if ( v < total ) {
stats.text( v + 1 );
} else {
$.getJSON('http://...', function(data) { // added data here
delta = Math.floor( 300000 / ( data.total_sessions - total ) );
total = data.total_sessions;
});
}
setTimeout(increment, delta);
}
Update:
In order to test my code, I had to simulate the JSON reponse - I used an array of numbers. See here: http://jsfiddle.net/simevidas/MwQKM/
(In the demo, I use an interval of 5 seconds instead of 5 minutes.)
I am not exactly sure why your code doesn't work as expected, although I suspect that it has to do with line LAST_NUMBER_OF_SESSIONS = new_value;. I wrote something similar and it works fine. It's not that different from what you have, minus that last line of code.

Categories

Resources