I have an animated counter number that works fine on mouseover but I have 2 problems with it:
I can't find a way to add mouse out to see the number decrease when the mouse is out and increase when the mouse is over
How to have 3 numbers visible, I mean starting with 000 then 001, 002... until 99 then 100, 101, 102... and not 000 then 1, 2, 3...
Here is my code
function animationEffect(){
const counters = document.querySelectorAll('.counter');
for(let n of counters) {
const updateCount = () => {
const target = + n.getAttribute('data-target');
const count = + n.innerText;
const speed = 3000; // change animation speed here
const inc = target / speed;
if(count < target) {
n.innerText = Math.ceil(count + inc);
setTimeout(updateCount, 1);
} else {
n.innerText = target;
}
}
updateCount();
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<span>Kal /</span>
<span id="animate" style="font-size: 200px;" class="counter" data-target="652" onmouseover="animationEffect();">000</span>
Related
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?
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>
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>
The goal is to make the button turn off if pressed 10 times in less than 1 minute but keep counting if not pressed 10 times in 1 minute? so only disables when pressed 10 times in less than 1 minute. You can click nine times in a minute but nothing happens but the tenth time the button turns off, but if the minute has passed you can keep clicking but always for a maximum ten times and the counter does not reset but continues to increase in number.
let accCounter = 0;
let totalCount = 0;
document.getElementById('totalCounter').innerText = totalCount;
document.getElementById('clap').onclick = function() {
const clap = document.getElementById('clap');
const clickCounter = document.getElementById("clicker");
upClickCounter();
function upClickCounter() {
const clickCounter = document.getElementById("clicker");
const totalClickCounter = document.getElementById('totalCounter');
accCounter++;
clickCounter.children[0].innerText = '+' + accCounter;
totalClickCounter.innerText = totalCount + accCounter;
}
}
var endCount = 5; // 5 for testing - this would be 10
var interval = 5000 // 5s for testing, 1 min= 1000 * 60;
var clicks = [];
var totalClicks = 0;
$("#btn").click(function() {
clicks.push(Date.now());
totalClicks++;
checkIt();
})
function checkIt() {
//if (clicks.length < endCount) return;
while (clicks.length && (Date.now() - clicks[0]) > interval) {
console.log("removing: " + ((Date.now() - clicks[0]) / 1000))
clicks.shift();
}
if (clicks.length < endCount) return;
$("#btn")[0].disabled = true;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="clap" class="clap-container"></button>
<div id="totalCounter" class="total-counter"></div>
<div id="clicker" class="click-counter"></div>
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>