You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JS大小数值计数器 实现不同速度运行且动画同步结束

问题原因

原有计数逻辑存在两个核心问题,导致不同目标值的计数器动画不同步:

  • 未统一约束总动画时长:代码固定setTimeout间隔为50ms,单步增量设置为目标值/1000,配合Math.ceil向上取整后,小目标值计数器仅需极少步数就能到达终点,大目标值计数器需要的步数是小数值的上百倍,总耗时差距可达数十倍。
  • 增量与取整逻辑不匹配:小目标值计算出的单步增量小于1,Math.ceil会直接将累加后的小数向上取整为整数,相当于单次跳过多个计数步长,动画过程被压缩到几乎不可见。
修复方案

核心逻辑是给所有计数器设置统一的总动画时长,基于动画已运行时间占总时长的比例,实时计算每个计数器当前应显示的数值,保证所有计数器同步从0增长到目标值、同时结束动画。使用requestAnimationFrame替代固定间隔的setTimeout,动画流畅度更高,时间控制更精准。

修正后完整JS代码

let section_counter = document.querySelector('#section_counter');
let counters = document.querySelectorAll('.counter-item .counter');
// 所有计数器统一动画总时长,单位毫秒,可按需调整
const ANIMATION_DURATION = 2000;

// 滚动触发逻辑
let CounterObserver = new IntersectionObserver(
  (entries, observer) => {
    let [entry] = entries;
    if (!entry.isIntersecting) return;

    // 记录动画启动时间戳
    const startTime = performance.now();

    function updateCounters() {
      // 计算当前动画进度,取值范围0-1
      const elapsed = performance.now() - startTime;
      const progress = Math.min(elapsed / ANIMATION_DURATION, 1);

      // 统一更新所有计数器的当前值
      counters.forEach(counter => {
        const targetNumber = +counter.dataset.target;
        // 按进度计算当前显示值,整数场景用round取整即可
        const currentValue = Math.round(targetNumber * progress);
        counter.innerText = currentValue;
      })

      // 进度未到100%则继续执行下一帧动画,到达终点后终止
      if (progress < 1) {
        requestAnimationFrame(updateCounters);
      }
    }

    // 启动计数动画
    requestAnimationFrame(updateCounters);

    // 保留原有元素滑入动画逻辑
    counters.forEach((counter, index) => {
      if (counter.parentElement.style.animation) {
        counter.parentElement.style.animation = '';
      } else {
        counter.parentElement.style.animation = `slide-up 0.3s ease forwards ${
          index / counters.length + 0.5
        }s`;
      }
    });

    observer.unobserve(section_counter);
  },
  {
    root: null,
    threshold: window.innerWidth > 768 ? 0.4 : 0.3,
  }
);

CounterObserver.observe(section_counter);

调整说明

  • 所有计数器共用同一套时间进度计算逻辑,无论目标值是15还是9000,都会在设定的ANIMATION_DURATION时长内完成计数,天然保持同步结束。
  • 用requestAnimationFrame替代setTimeout,动画和浏览器刷新频率对齐,不会出现卡顿、跳帧问题。
  • 去掉原逻辑中每个计数器单独递归更新的写法,统一在单帧内更新所有计数器数值,执行性能更高。
  • 如果后续需要支持小数目标值,仅需替换取整逻辑即可,比如保留1位小数就将Math.round(targetNumber * progress)改为Math.round(targetNumber * progress * 10)/10,不影响多计数器同步效果。

原有HTML结构不需要做任何修改,直接替换JS代码即可生效。


内容的提问来源于stack exchange,提问作者Mario Sosa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 05:18:19