admin管理员组文章数量:1135242
I am updating a numeric value
inside an element
by doing intervalled ajax requests
.
To make the whole thing a bit more alive, I want to count from the current
value to the new
one, by partially in- or decreasing the value
over a time of n sec
.
So basically something like this:
<div id="value">100</div>
<script type="text/javascript">
/** Decrease $value (over a time of 2 seconds) till it reaches 25 */
$value.increaseAnimation(-75, {duration:2});
</script>
Is there a javascript library for doing so?
I am updating a numeric value
inside an element
by doing intervalled ajax requests
.
To make the whole thing a bit more alive, I want to count from the current
value to the new
one, by partially in- or decreasing the value
over a time of n sec
.
So basically something like this:
<div id="value">100</div>
<script type="text/javascript">
/** Decrease $value (over a time of 2 seconds) till it reaches 25 */
$value.increaseAnimation(-75, {duration:2});
</script>
Is there a javascript library for doing so?
Share Improve this question edited Mar 26, 2021 at 22:37 doğukan 27.3k13 gold badges62 silver badges75 bronze badges asked Jun 8, 2013 at 0:09 RienNeVaPlu͢sRienNeVaPlu͢s 7,6326 gold badges45 silver badges78 bronze badges 2- 3 This tiny script does just that - github.com/yairEO/Do-in – vsync Commented Sep 22, 2015 at 12:30
- 3 For those who have been looking for a nice library there is countup.js. – johannchopin Commented Jan 16, 2022 at 22:51
11 Answers
Reset to default 123You can just code it yourself pretty simply:
function animateValue(id, start, end, duration) {
if (start === end) return;
var range = end - start;
var current = start;
var increment = end > start? 1 : -1;
var stepTime = Math.abs(Math.floor(duration / range));
var obj = document.getElementById(id);
var timer = setInterval(function() {
current += increment;
obj.innerHTML = current;
if (current == end) {
clearInterval(timer);
}
}, stepTime);
}
animateValue("value", 100, 25, 5000);
#value {
font-size: 50px;
}
<div id="value">100</div>
Here's is a more accurate version that self adjusts in case the timer intervals aren't perfectly accurate (which they sometimes aren't):
function animateValue(id, start, end, duration) {
// assumes integer values for start and end
var obj = document.getElementById(id);
var range = end - start;
// no timer shorter than 50ms (not really visible any way)
var minTimer = 50;
// calc step time to show all interediate values
var stepTime = Math.abs(Math.floor(duration / range));
// never go below minTimer
stepTime = Math.max(stepTime, minTimer);
// get current time and calculate desired end time
var startTime = new Date().getTime();
var endTime = startTime + duration;
var timer;
function run() {
var now = new Date().getTime();
var remaining = Math.max((endTime - now) / duration, 0);
var value = Math.round(end - (remaining * range));
obj.innerHTML = value;
if (value == end) {
clearInterval(timer);
}
}
timer = setInterval(run, stepTime);
run();
}
animateValue("value", 100, 25, 5000);
#value {
font-size: 50px;
}
<div id="value">100</div>
Current solutions do update more often than needed. Here a frame based approach, which is accurate:
function animateValue(obj, start, end, duration) {
let startTimestamp = null;
const step = (timestamp) => {
if (!startTimestamp) startTimestamp = timestamp;
const progress = Math.min((timestamp - startTimestamp) / duration, 1);
obj.innerHTML = Math.floor(progress * (end - start) + start);
if (progress < 1) {
window.requestAnimationFrame(step);
}
};
window.requestAnimationFrame(step);
}
const obj = document.getElementById('value');
animateValue(obj, 100, -25, 2000);
div {font-size: 50px;}
<div id="value">100</div>
Now we can animate counters (and a lot of things that cannot be animated before) with CSS variables and new @property. No javascript needed. Currently supports only Chrome and Edge.
@property --n {
syntax: "<integer>";
initial-value: 0;
inherits: false;
}
body {
display: flex;
}
.number {
animation: animate var(--duration) forwards var(--timing, linear);
counter-reset: num var(--n);
font-weight: bold;
font-size: 3rem;
font-family: sans-serif;
padding: 2rem;
}
.number::before {
content: counter(num);
}
@keyframes animate {
from {
--n: var(--from);
}
to {
--n: var(--to);
}
}
<div class="number" style="--from: 0; --to: 100; --duration: 2s;"></div>
<div class="number" style="--from: 10; --to: 75; --duration: 5s; --timing: ease-in-out"></div>
<div class="number" style="--from: 100; --to: 0; --duration: 5s; --timing: ease"></div>
I had a slightly different approach to this kind of animation. Based on these assumptions:
- there is a starting text (as fallback for non-JS browser or google indexing)
- this text can contains non-numeric chars
- the function take an element directly as first param (as opposed to an element Id)
So, if you want to animate a simple text like "+300% gross margin", only the numeric part will be animated.
Moreover, all params have now a default value for start
, end
and duration
.
https://codepen.io/lucamurante/pen/gZVymW
function animateValue(obj, start = 0, end = null, duration = 3000) {
if (obj) {
// save starting text for later (and as a fallback text if JS not running and/or google)
var textStarting = obj.innerHTML;
// remove non-numeric from starting text if not specified
end = end || parseInt(textStarting.replace(/\D/g, ""));
var range = end - start;
// no timer shorter than 50ms (not really visible any way)
var minTimer = 50;
// calc step time to show all interediate values
var stepTime = Math.abs(Math.floor(duration / range));
// never go below minTimer
stepTime = Math.max(stepTime, minTimer);
// get current time and calculate desired end time
var startTime = new Date().getTime();
var endTime = startTime + duration;
var timer;
function run() {
var now = new Date().getTime();
var remaining = Math.max((endTime - now) / duration, 0);
var value = Math.round(end - (remaining * range));
// replace numeric digits only in the original string
obj.innerHTML = textStarting.replace(/([0-9]+)/g, value);
if (value == end) {
clearInterval(timer);
}
}
timer = setInterval(run, stepTime);
run();
}
}
animateValue(document.getElementById('value'));
#value {
font-size: 50px;
}
<div id="value">+300% gross margin</div>
const counters = document.querySelectorAll('.counters');
counters.forEach(counter => {
let count = 0;
const updateCounter = () => {
const countTarget = parseInt(counter.getAttribute('data-counttarget'));
count++;
if (count < countTarget) {
counter.innerHTML = count;
setTimeout(updateCounter, 1);
} else {
counter.innerHTML = countTarget;
}
};
updateCounter();
});
<p class="counters" data-counttarget="50"></p>
<p class="counters" data-counttarget="100"></p>
<p class="counters" data-counttarget="500"></p>
<p class="counters" data-counttarget="1000"></p>
This works well. However, I needed to use a comma within the number. Below is the updated code which checks for commas. Hope someone finds this useful if they stumble across this post.
function animateValue(id, start, end, duration) {
// check for commas
var isComma = /[0-9]+,[0-9]+/.test(end);
end = end.replace(/,/g, '');
// assumes integer values for start and end
var obj = document.getElementById(id);
var range = end - start;
// no timer shorter than 50ms (not really visible any way)
var minTimer = 50;
// calc step time to show all interediate values
var stepTime = Math.abs(Math.floor(duration / range));
// never go below minTimer
stepTime = Math.max(stepTime, minTimer);
// get current time and calculate desired end time
var startTime = new Date().getTime();
var endTime = startTime + duration;
var timer;
function run() {
var now = new Date().getTime();
var remaining = Math.max((endTime - now) / duration, 0);
var value = Math.round(end - (remaining * range));
obj.innerHTML = value;
if (value == end) {
clearInterval(timer);
}
// Preserve commas if input had commas
if (isComma) {
while (/(\d+)(\d{3})/.test(value.toString())) {
value = value.toString().replace(/(\d+)(\d{3})/, '$1'+','+'$2');
}
}
}
var timer = setInterval(run, stepTime);
run();
}
animateValue("value", 100, 25, 2000);
HTML
<!DOCTYPE html>
<html>
<head>
<title>Count</title>
</head>
<body>
<div id="value">1000</div>
</body>
</html>
JAVASCRIPT snippet
Here is a simple js function decrementing values from a given start number to an end number (object prototype)..
function getCounter(startCount,endcount,time,html){
objects = {
//you can alternateif you want yo add till you reach the endcount
startCount:startCount,
endCount:endcount,
timer:time
}
this.function = function(){
let startTm = objects.startCount,
timer = objects.timer,
endCount = objects.endCount;
//if you want it to add a number just replace the -1 with +1
/*and dont forget to change the values in the object prototype given a variable of counter*/
let increment = startTm < endCount ? 1:-1;
timmer = setInterval(function(){
startTm += increment;
html.innerHTML = startTm ;
if(startTm == endCount){
clearInterval(timmer);
}
},timer);
}
}
// input your startCount,endCount the timer.....
let doc = document.getElementById('value');
let counter = new getCounter(1000,1,10,doc);
//calling the function in the object
counter.function();
Check out this demo https://jsfiddle.net/NevilPaul2/LLk0bzvm/
This is what i came up with:
function animateVal(obj, start=0, end=100, steps=100, duration=500) {
start = parseFloat(start)
end = parseFloat(end)
let stepsize = (end - start) / steps
let current = start
var stepTime = Math.abs(Math.floor(duration / (end - start)));
let stepspassed = 0
let stepsneeded = (end - start) / stepsize
let x = setInterval( () => {
current += stepsize
stepspassed++
obj.innerHTML = Math.round(current * 1000) / 1000
if (stepspassed >= stepsneeded) {
clearInterval(x)
}
}, stepTime)
}
animateVal(document.getElementById("counter"), 0, 200, 300, 200)
Here is one version where increments grow by some defined multiplier (mul). frameDelay is the time delay for each increment. This looks a bit better if you have values that camn
function cAnimate(id, start, end, frameDelay = 100, mul = 1.2) {
var obj = document.getElementById(id);
var increment = 2;
var current = start;
var timer = setInterval(function() {
current += increment;
increment *= mul;
if (current >= end) {
current = end;
clearInterval(timer);
}
obj.innerHTML = Math.floor(current).toLocaleString();
}, frameDelay);
}
cAnimate("counter", 1, 260000, 50);
I used a mixture of all of these to create a function that updates a BehaviorSubject.
function animateValue(subject, timerRef, startValue, endValue, duration){
if (timerRef) { clearInterval(timerRef); }
const minInterval = 100;
const valueRange = endValue - startValue;
const startTime = new Date().getTime();
const endTime = startTime + (duration * 1000);
const interval = Math.max((endTime-startTime)/valueRange, minInterval);
function run() {
const now = new Date().getTime();
const rangePercent = Math.min(1-((endTime-now)/(endTime-startTime)),1);
const value = Math.round(rangePercent * valueRange+startValue);
subject.next(value);
if (rangePercent >= 1) {
clearInterval(timerRef);
}
}
timerRef = setInterval(run, interval);
run();
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
*{
margin: 0;
padding: 0;
}
</style>
</head>
<body style="width: 100vw; height: 100vh; display: flex; align-items: center; justify-content: center;">
<h1 style="font-size: 50px;" data-value="3212">0</h1>
<script>
let e = document.querySelector('h1');
let v = Number(e.dataset.value);
let i = Math.floor(v/10);
let r = v%10;
function increment() {
let c = Number(e.innerText);
if (c<v) {
if (v-c===i+r) {
e.innerText = c+i+r;
}
else{
e.innerText = c+i;
};
setTimeout(increment,200);
};
};
setTimeout(increment, 200);
</script>
</body>
</html>
本文标签: javascriptCount animation from number A to BStack Overflow
版权声明:本文标题:javascript - Count animation from number A to B - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736920505a1956446.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论