how can I code counting animation with comma in vanilla javascript? - 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>

Related

How to make animated counter using 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?

How to link a slider and checkbox to a counter?

I am new in JavaScript. I am trying to link a slider and checkbox to a counter. The slider should increase the counter's value depending on the range. And then the checkboxes have fixes values depending on the users(the slider's value are users, I will explain later) that they should add to the counter if it is checked or not.
var slider = document.querySelector('#myRange'); //Input
var output = document.querySelector("#value-range"); //Output
let rangeValue = document.querySelector('#final-price'); //Counter
const boxesContainer = document.querySelector('.price-boxes__container'); //Checkboxes
I created an event for the checkboxes, so when you click on it, adds the value.
boxesContainer.onchange = function(e) {
if (e.target.classList.contains('checkbox')){
const price = parseInt(e.target.getAttribute('value'))
if (e.target.checked === true) {
total += price;
} else {
total -= price;
}
rangeValue.innerHTML = total
}
}
And I created another event for the slider as well.
slider.oninput = function () {
let value = slider.value;
userPrice(value)
output.style.left = (value/2.88);
output.classList.add("show");
rangeValue.innerHTML = prices[value-25];
function userPrice (value) {
if (value == 25) {
output.innerHTML = '6 €p'
} else {
output.textContent = `${prices[value-25] - prices[value-26]} €p`;
}
}
}
The slider has min 25, max 1000, step = 1, value = 25. Each step is one user. I created an array that has the price depending on the slider's value. Between 26-50 users 6€ per user, 51-250 4€/p, 251-500 3€/p and 501-1000 2€/p. Therefore, I thought the easy way was to create an array and use the slider's value to sent the price to the counter.
const range = (start, stop, step) => Array.from({ length: (stop - start) / step + 1}, (_, i) => start + (i * step));
let rangePrices = range(25, 1000, 1);
const prices = [];
rangePrices.forEach((rangeValue, i)=> {
if(rangeValue === 25) {
prices.push(299)
} else if (rangeValue < 51) {
prices.push(prices[i-1] + 6)
} else if (rangeValue < 251){
prices.push(prices[i-1] + 4)
} else if (rangeValue < 501) {
prices.push(prices[i-1] + 3)
} else {
prices.push(prices[i-1] + 2)
}
});
But at the end, when I click on the checkboxes the counter adds the checkboxes' values, but it resets. I know that I have two rangeValue.innerHTML and is due to this that it does not work, but I do not know how to fix it...
As I said at the beginning the checkbox depends on the slider value. Between 26-50 0.7€/p.u., 51-250 0.5€/p.u., 251-500 0.4€/p.u. and 501-1000 0.3€/p.u.. Therefore, so the checkboxes are related to the slider. I do not know how to link both functions either. Finally say that there are 2 more checkboxes.
It would be great if someone could help me!
https://jsfiddle.net/NilAngelats/wq7tjh8c/
This is what I did:
let total = 0;
boxesContainer.onchange = function(e) {
...
rangeValue.innerHTML = prices[slider.value - 25] + total
}
slider.oninput = function() {
...
rangeValue.innerHTML = prices[value - 25] + total;
}
And move userPrice function out of the slider.oninput function so it's only declared once and not every time you move the slider.

Javascript counter up with various setTimeout

I created from various sources the simplest js counter up I could find. How can I make different setTimeout for each counter so both of them end at the same time?
UPDATE: I have minimized the right answer below and this is the final code:
const counters = document.querySelectorAll('.counter');
const duration = 2000; // Finish in 2 seconds
const speed = 2000;
const state = {};
const max = Math.max(...[...counters]
.map(counter => +counter.dataset.target));
const tick = duration / max;
const updateCount = (counter) => {
const target = +counter.dataset.target;
const value = +counter.dataset.value;
const ratio = target / max;
const ms = Math.ceil(ratio * tick);
const incr = target / speed;
const newVal = value + incr;
if (value < target) {
counter.innerText = Math.floor(newVal);
counter.dataset.value = newVal;
setTimeout(updateCount, ms, counter);
} else {
counter.innerText = target;
counter.dataset.value = target;
}
};
counters.forEach(updateCount);
<div class="counter" data-key="1" data-value="0" data-target="1000">0</div>
<div class="counter" data-key="2" data-value="0" data-target="2000">0</div>
Each timer has to run at an increases rate relative to the max time.
This is a rudimentary example, and it still may need some work. I also recmomment using the dataset property to store the value and only re-render the value (in case the result is a floating-point number).
const results = document.querySelector('.results');
const counters = document.querySelectorAll('.counter');
const duration = 2000; // Finish in 2 seconds
const speed = 1000;
const state = {};
const max = Math.max(...[...counters]
.map(counter => +counter.dataset.target));
const tick = duration / max;
const updateCount = (counter) => {
const target = +counter.dataset.target;
const value = +counter.dataset.value;
const ratio = target / max;
const ms = Math.ceil(ratio * tick);
const incr = target / speed;
const newVal = value + incr;
const { dataset: { key }} = counter;
state[key] = {
...state[key],
ratio, ms, incr, value
};
results.textContent = JSON.stringify(state, null, 2);
if (value < target) {
counter.innerText = Math.floor(newVal);
counter.dataset.value = newVal;
setTimeout(updateCount, ms, counter);
} else {
counter.innerText = target;
counter.dataset.value = target;
}
};
counters.forEach(updateCount);
.results {
border: thin solid grey;
padding: 0.25em;
white-space: pre;
font-family: monospace;
}
<div class="counter" data-key="1" data-value="0" data-target="1000">0</div>
<div class="counter" data-key="2" data-value="0" data-target="2000">0</div>
<div class="counter" data-key="3" data-value="0" data-target="4000">0</div>
<div class="results"></div>
It works like this because your setTimeout(updateCount, 100); runs after 100 miliseconds. So when you are counting from 0 to 150 and you are adding a number once per 100ms it will be twice faster as counting to 300.
You can make it end at the same time, when you change setTimeout() to run after 50 miliseconds for counting to 300
Something like this
setTimeout(updateCount150, 100);
setTimeout(updateCount300, 50);
Of course you need to adjust those both functions accordingly.
Try getting the avg, like this:
const counters = document.querySelectorAll('.counter');
const speed = 1;
let avg=0;
counters.forEach(counter => {
avg+=parseInt(counter.getAttribute('data-target'))
});
avg=avg/counters.length;
counters.forEach(counter => {
const updateCount = () => {
const count = +counter.innerText;
const target = +counter.getAttribute('data-target')
const inc = counter.getAttribute('data-target') / (speed * avg);
if (count < target) {
counter.innerText = count + inc;
setTimeout(updateCount, 100);
} else {
counter.innerText = target;
}
};
updateCount();
});
<div class="counter" data-target="150">0</div>
<div class="counter" data-target="300">0</div>

React useState method in function doesn't work as expected

I'm new to ReactJS and I have some problem with react function. I have simple counter that changes current number depending on the button you clicked. It works fine except the check on minimum and maximum value. Here is my code:
import React, { useState } from 'react';
export default function CounterFunction(props) {
const {min, max} = props;
const [cnt, setCnt] = useState(min);
const decrease = () => {
setCnt(set(cnt - 1));
}
const increase = () => {
setCnt(set(cnt + 1));
}
let set = function(newCnt) {
console.log("TCL: set -> newCnt", newCnt)
let cnt = Math.min(Math.max(newCnt, min), max);
setCnt(cnt);
}
return (
<div>
<button onClick={decrease}>Minus 1</button>
<strong>{cnt}</strong>
<button onClick={increase}>Plus 1</button>
</div>
)
}
And here is App component:
import React from 'react';
import MinMaxFunction from './carts/minmax';
export default function() {
return (
<div>
<MinMaxFunction min={2} max={10} />
</div>
);
}
When I try to increase or decrease number it turns into NaN. Any help would be appreciated.
const decrease = () => {
setCnt(set(cnt - 1));
}
const increase = () => {
setCnt(set(cnt + 1));
}
let set = function(newCnt) {
console.log("TCL: set -> newCnt", newCnt)
let cnt = Math.min(Math.max(newCnt, min), max);
return cnt; // return
}
You need to just return cnt from set.
In set you are setting cnt to desired value but returning nothing hence undefined. In decrease and increase you are setting cnt to return value of set which is undefined hence NaN.
Alternate way of doing same thing:
const decrease = () => {
set(cnt - 1); // call the set function, no need of setCnt here
}
const increase = () => {
set(cnt + 1);
}
let set = function(newCnt) {
console.log("TCL: set -> newCnt", newCnt)
let cnt = Math.min(Math.max(newCnt, min), max);
setCnt(cnt); // set state just here
}
Your function set is returning undefined, because you don't have an return statement there. And you are setting undefined in your setCnt.
You don't need to pass set into setCnt, because you are using setCnt inside set. So change the code to:
const decrease = () => { set(cnt - 1); }
The set function should return value which is the new number. You didn't use return so the computer assumes you're returning nothing. So you need to do return cnt;

How to generate non-recurring numbers from the range JavaScript

I have array with 10 items, I calls one random item using a random number from 1 to 10, what to use to make a random number from 1 to 10, which will not happen again, and when all 10 is used, the program stops randomly? code
const num = () => Math.floor(Math.random() * 10);
const postWord = () => {
randomWord = word.innerText = words[num()].PL;
}
postWord();
submit.addEventListener("click", function() {
postWord();
});
Have you ever considered to move the array items?
var range = 10; // global variable
const num = () => Math.floor(Math.random() * range);
const postWord = () => {
randomWord = word.innerText = words[num()].PL;
for (var i=num(); i < range; i++) {
var temp = words[i];
words[i] = words[i+1];
words[i+1] = temp;
}
range--;
}
postWord();
submit.addEventListener("click", function() {
if (range) {
postWord();
}
});
I am not that familiar with JS but I guess my code can at least demonstrate my point.

Categories