How to make animated counter using JavaScript? - javascript

I have a problem with this code. 4.9 never comes out, only 4.5 or 5.5. Could it be possible to spin up the decimals instead of the integers to reach 4.9?
let counts = setInterval(updated, 500);
let upto = 1.5;
function updated() {
var count = document.getElementById("counter");
count.innerHTML = ++upto;
if (Number(upto.toFixed(1)) >= 4.9) {
clearInterval(counts);
}
}

First: do not increment by 1 (++upto === upto + 1).
Second: You need to modify your clearInterval condition:
let counts=setInterval(updated, 10);
let upto=70.1;
function updated(){
var count= document.getElementById("counter");
count.innerHTML=upto + 0.1;
if(Number(upto.toFixed(1))===88.5)
{
clearInterval(counts);
}
}

You had two small but important issues that I fixed. Read the comments I put in the code snippet.
Updated: Use toFixed to show x count of decimals.
let counts = setInterval(updated, 10);
let upto = 70.1;
var count = document.getElementById("counter");
function updated() {
// ++upto would never hit 88.5, it will hit 88.1 89.1 -> so we do += 0.1
// Updated: Show only one decimal using toFixed()
const _t = upto += 0.1
count.innerHTML = _t.toFixed(1);
// Changing upto to number again because toFixed converts it to string
if (Number(upto.toFixed(1)) === 88.5) {
clearInterval(counts);
}
}
<p id="counter"></p>

Use a from step to variables, and make sure to reset the curr value to a to value when it exceeds that max:
// Counter options:
const from = 0;
const step = 0.1;
const to = 4.9;
const speed = 100; // ms
// Counter:
const elCounter = document.getElementById("counter");
let itvCounts = null;
let curr = from;
const updateCounter = () => {
curr += step;
if (curr >= to) {
clearInterval(itvCounts);
curr = to;
}
elCounter.innerHTML = +curr.toFixed(1);
};
// Init:
itvCounts = setInterval(updateCounter, speed);
<span id="counter"></span>
Find more useful examples and implementations here:
Animate counter from start to end value
How to restart counter animation when it's out of view?

Related

Playing animation during x seconds using setInterval

I want to display an animated number from 0 to max value y during x seconds. I have tried this following code but it take too much to complete and clear the interval.
jQuery('.numbers').each(function(item, index) {
const $obj = jQuery(this);
let objValue = parseInt($obj.text()),
currentValue = 0,
speed = 1,
time = 4000,
step = Math.floor(objValue / time);
$obj.text(currentValue);
let interVal = setInterval(() => {
if (currentValue >= objValue) {
clearInterval(interVal);
$obj.text(objValue);
}
$obj.text(currentValue);
currentValue += step
}, speed);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<span class='numbers'>7586</span>
<span class='numbers'>147520</span>
How do I play this animation during exactly time seconds?
It is better not to depend on the timing of setInterval(), but the real problem in your script is that you use the floored value to decide the new value to print out.
It is better to use Window.requestAnimationFrame() and create a update() function that prints the current number based on the real time elapsed.
let start, previousTimeStamp;
let numbers = document.querySelectorAll('.numbers');
requestAnimationFrame(update);
function update(timestamp) {
if (start === undefined) {
start = timestamp;
}
const elapsed = timestamp - start;
[...numbers].forEach(elm => {
if(!elm.dataset.start){
elm.dataset.start = elm.textContent;
}
let start = parseInt(elm.dataset.start);
elm.textContent = Math.floor(start / 4000 * elapsed);
});
if (elapsed < 4000) {
previousTimeStamp = timestamp;
requestAnimationFrame(update);
}else {
start = undefined;
[...numbers].forEach(elm => {
elm.textContent = elm.dataset.start;
});
}
}
<span class='numbers'>7586</span>
<span class='numbers'>147520</span>

how can I code counting animation with comma in vanilla javascript?

I made counting animations! But, the Designer asked them to take commas every three digits, so I wrote a code to take commas, but I think it should be uploaded in real-time, not just at the end. I'm not used to JavaScript yet. ㅜㅜ How should I fix it?
function counterAnimationHandler() {
const counters = document.querySelectorAll('.counter ')
counters.forEach(counter => {
counter.innerText = '0' //set default counter value
const updateCounter = () => {
const target = +counter.getAttribute('data-target') //define increase couter to it's data-target
const count = +counter.innerText //define increase couter on innerText
const increment = target / 200 // define increment as counter increase value / speed
if (count < target) {
counter.innerText = `${Math.ceil(count + increment)}`;
setTimeout(updateCounter, 1);
} else {
counter.innerText = numberWithCommas(target); //if default value is bigger that date-target, show data-target
}
}
updateCounter() //call the function event
})
function numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
}
counterAnimationHandler();
<div class="counter" data-target="1000000"></div>
I would suggest you keepp a different variable for count with the raw (unformatted) number and then make sure you wrap every update to the UI with numberWithCommas.
function counterAnimationHandler() {
const counters = document.querySelectorAll('.counter ')
counters.forEach(counter => {
counter.innerText = '0' //set default counter value
counter.dataset.count = 0;
const updateCounter = () => {
const target = +counter.getAttribute('data-target') //define increase couter to it's data-target
const count = +counter.dataset.count //define increase couter on innerText
const increment = target / 200 // define increment as counter increase value / speed
if (count < target) {
const newCount = Math.ceil(count + increment);
counter.dataset.count = newCount;
counter.innerText = numberWithCommas(newCount);
setTimeout(updateCounter, 1);
} else {
counter.innerText = numberWithCommas(target); //if default value is bigger that date-target, show data-target
}
}
updateCounter() //call the function event
})
function numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
}
counterAnimationHandler();
<div class="counter" data-target="1000000"></div>

How do I refresh the contents of an array?

I have an array which is taking values over the space of a second and calculating the average. The array is then permanently storing the values. I need them to remove the values after the average has been calculated. I have tried clearing the array at the end of the calculateAverage function using levelMeasurements = []; and levelMeasurements = 0; but that does not work.
Any help on this would be greatly appreciated, thanks!
My code:
var levelMeasurements = [];
function collectLevelMeasurements() {
levelMeasurements.push(averagingLevel);
}
var collectInterval = window.setInterval(collectLevelMeasurements, 0);
function calculateAverage() {
window.clearInterval(collectInterval);
var avg = 0;
for (counter = 0; counter < levelMeasurements.length; counter++) {
avg += levelMeasurements[counter] / levelMeasurements.length;
}
averageAbsoluteLevel = Math.abs(avg);
averageDbLevel = Tone.gainToDb(averageAbsoluteLevel) * scale + offset;
console.log("Measure for 5 minutes: average level is:" + averageDbLevel);
}
window.setInterval(calculateAverage, 1000);
Your collectInterval timeout is never reinstated after the first time calculateAverage is called, unless the issue is something else not visible from the code.
In your code calculateAverage is called every 1000 milliseconds, but the values stored in levelMeasurements are not updated after the first window.clearInterval.
I made a simplified snippet without Tone lib:
var levelMeasurements = [];
//added this as a easy source of values
let count = 0;
function collectLevelMeasurements() {
// levelMeasurements.push(averagingLevel);
levelMeasurements.push(count++);
}
var collectInterval = window.setInterval(collectLevelMeasurements, 0);
function calculateAverage() {
window.clearInterval(collectInterval);
var avg = 0;
for (counter = 0; counter < levelMeasurements.length; counter++) {
avg += levelMeasurements[counter] / levelMeasurements.length;
}
averageAbsoluteLevel = Math.abs(avg);
averageDbLevel = averageAbsoluteLevel;
//prolly dont need tone lib for this example
// averageDbLevel = Tone.gainToDb(averageAbsoluteLevel) * scale + offset;
console.log("Measure for 5 minutes: average level is:" + averageDbLevel);
//reset these variables
levelMeasurements = [];
count = 0;
console.log(levelMeasurements)//empty array at this point
//reset the collectInterval!
collectInterval = window.setInterval(collectLevelMeasurements, 0);
}
window.setInterval(calculateAverage, 1000);
You can try following:
levelMeasurements.length = 0.
Or refer more solution at here: How do I empty an array in JavaScript?

What's wrong with this Codility Answer?

The question was to find the largest binary gap in a number, and while this worked in my IDE, Codility didn't accept it. Any thoughts?
const biGap = (number) => {
const binary = number.toString(2)
const re = /1(0+)1/g;
const found = binary.match(re)
let counter = 0
found.map((match) => {
if (match.length > counter) {
counter = match.length
}
})
return (counter - 2);
}
console.log(biGap(1041));
The main problem with your code is that binary.match(re) won't return overlapping matches. So if binary = "1010000001001", it will return ["101", "1001"], which is missing the long gap 10000001 between them.
You can solve this by changing the regexp to
const re = /0+1/g;
Then you should return counter - 1 instead of counter - 2.
You don't need to put 1 on both sides of the 0+ because number.toString(2) will never include leading zeroes, so there's always a 1 to the left of any string of zeroes, and it's not necessary to match it explicitly.
If you also want to include the binary gap in the low order bits of the number, you can change the regexp to simply:
const re = /0+/g;
Then you don't need to subtract anything from counter when returning.
const biGap = (number) => {
const binary = number.toString(2)
const re = /0+1/g;
const found = binary.match(re)
let counter = 0
found.map((match) => {
if (match.length > counter) {
counter = match.length
}
})
return (counter - 1);
}
console.log(biGap(1041));
console.log(biGap(parseInt("1010000001001", 2))); // Your code returns 2
function binGap(N) {
var max=0;
var count=0;
var binary = Number(N).toString(2);;
Array.prototype.forEach.call(binary,function(i) {
if(i == 0) {
count++;
} else {
if(count > max) {
max = count;
}
count = 0;
}
});
return max;
}
for starters make sure that the regex returns all the right candidates
map operator is used the wrong way. Reduce is the way to use in your case
you should not subtract 2 from the counter at return, instead do that in the reduce callback
don't console.log
And a final thought, why convert the number into string? why not use modulo 2? it's way simpler and efficient. (think of how much resources a regex requires)
a possible solution may be this
solution(N) {
while(N && N%2 === 0) {
N = N>>1
}
let counter = 0;
let tempCounter = 0;
let n=N;
while(n) {
if(n%2 === 1) {
counter = Math.max(counter, tempCounter);
tempCounter=0;
} else {
tempCounter = tempCounter + 1;
}
n = n>>1;
}
return counter;
}

Make number grow with jQuery using same amount of time for every number

My goal is to perform a jQuery script that'll make any number visually grow from zero until its value with setInterval().
Here's what I came up with:
$('.grow').each(function() {
var $el = $(this);
var max = parseInt($el.text().replace(/\s/g, ''));
var refresh = 5;
var start = 0;
var step = 1;
var interval = window.setInterval(function() {
start += step;
$el.text(start);
if(start >= max)
clearInterval(interval);
}, refresh);
});
My problem is I have numbers ranging from a few hundreds to several hundreds thousands. With this script, bigger number take more time to reach their value.
My goal is to make any number, regardless of its goal value, take the same amount of time to reach it. I sense that I should divide the number by the number of seconds I want the animation to run and then, set the result as the interval step?
I'm inquiring but still seeking for help :)
Thanks.
$('.grow').each(function() {
var $el = $(this);
var max = parseInt($el.text().replace(/\s/g, ''));
var duration = 1000; // shared duration of all elements' animation
var refresh = 5;
var frames = duration / refresh; // number of frames (steps)
var start = 0;
var step = Math.max(Math.round(max / frames), 1); // step should be >= 1
var interval = window.setInterval(function() {
if(start + step < max) {
start += step;
}
else {
start = max;
clearInterval(interval);
}
$el.text(start);
}, refresh);
});
This should work I suppose,
$('.grow').each(function() {
var $el = $(this);
var max = parseInt($el.text().replace(/\s/g, ''));
var start = 0;
var refresh = 5;
var totalSteps = 10;
var step = max/totalSteps;
function calculate(){
start += step;
$el.text(Math.round(start));
if(start < max){
setTimeout(function() {
calculate();
}, refresh);
}
}
calculate();
});
This will always finish in 100 steps. I.e. 100*refresh = 100*5 = 500 seconds.

Categories