从数字A到B进行数字动画计数。

54

我正在通过间隔的Ajax请求更新元素中的数字值

为了让整个过程更加生动,我想通过在n秒的时间内部分地增加或减少,从当前值新值进行计数。

所以基本上就像这样:

<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>

有没有JavaScript库可以实现这个功能?


3
这个微小的脚本就是这样 - https://github.com/yairEO/Do-in - vsync
3
对于那些一直在寻找好用的库的人,可以尝试使用countup.js - johannchopin
11个回答

123

你可以相当简单地自己编写代码:

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>

这里有一个更精确的版本,可以自动调整定时器间隔,以防定时器不完全准确(有时会出现这种情况):

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>


1
@RienNeVaPlus - 我添加了一个更准确的版本。 - jfriend00
谢谢你,jfriend,这个很好用——而且不需要一个庞大的插件系统 :) - NiCk Newman
+1 今天这个帮了我很大的忙,谢谢。我 (非常) 粗略地修改了它,使其可以处理浮点数和整数,否则它只会一直计数...!current += (end - current < increment? end - current : increment); - EvilDr
@Robolisk - 我建议您在新问题中描述您的要求。您可以在新问题中参考此答案,但请描述您想要以不同的方式完成的内容。 - jfriend00
第二个选项是最好的! - VanilJS
显示剩余4条评论

23

当前解决方案更新的频率比需要的更高。这里提出一种基于帧的方法,更为准确:

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>


8

现在我们可以使用CSS变量和新的@property来制作动画计数器(以及以前无法制作动画的许多其他元素)。无需JavaScript。目前只有Chrome和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>


它能在SVG元素上工作吗?比如:tspan - Raeesh Alam

6

我对这种动画有稍微不同的方法。基于以下假设:

  1. 存在一个起始文本(作为非JS浏览器或Google索引的备选项)
  2. 此文本可以包含非数字字符
  3. 该函数直接将元素作为第一个参数(而不是元素Id)

因此,如果您想要动画一个简单的文本,比如“+300%毛利率”,那么只有数值部分会被动画。

此外,所有参数现在都有startendduration的默认值。

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>


5

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>


请不要仅仅发布代码作为答案,还要提供解释您的代码是如何解决问题的。带有解释的答案通常更有帮助和更高质量,并且更有可能吸引赞同。 - Tyler2P

4

这个很好用。但是,我需要在数字中使用逗号。下面是更新后的代码,可以检查逗号。如果有人偶然看到此帖子,希望他们能找到有用的信息。

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

1

HTML

<!DOCTYPE html>
<html>
<head>
    <title>Count</title>
</head>
<body>
    <div id="value">1000</div>
</body>
</html>

JAVASCRIPT代码片段

这是一个简单的JavaScript函数,从给定的起始数字递减值到结束数字(对象原型)。

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();

请查看这个演示 https://jsfiddle.net/NevilPaul2/LLk0bzvm/


0
<!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>

0

这里有一个版本,其中增量按某个定义的乘数(mul)增长。 frameDelay是每个增量的时间延迟。如果您有一些值,这看起来会好一些。

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

0

这是我想出来的代码:

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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接