当前位置:首页 > 虚拟主机 > 正文

滚动数字js怎么实现?js数字滚动插件

滚动数字效果(Counting Animation)是前端开发中常见的交互需求,通常用于展示统计数据、价格、分数等动态变化的数值,实现这一效果的核心在于利用 JavaScript 的定时器或动画 API,在指定时间内将起始数值平滑过渡到目标数值。

核心实现原理

实现滚动数字主要依赖以下三个关键要素:

  1. 起始值与目标值:明确动画开始时的数字和结束时的数字。
  2. 持续时间:定义动画完成的总时长(毫秒)。
  3. 缓动函数(Easing Function):决定数字变化的速度曲线,如线性变化、先快后慢等,使动画更自然。

基础实现方案:使用 setInterval

这是最传统且兼容性最好的方法,通过定时器每隔固定时间更新一次 DOM 文本内容。

步骤 描述 代码逻辑示意
1 获取目标元素和参数 获取 DOM 节点,设置 start, end, duration
2 计算每次增量 step = (end start) / (duration / interval)
3 启动定时器 使用 setInterval 每隔 16ms 或 10ms 执行一次
4 更新 DOM 将当前计算出的数值格式化后写入 innerText
5 清除定时器 当当前值达到目标值时,调用

clearInterval 停止动画

滚动数字js怎么实现?js数字滚动插件 第1张

代码示例:

function animateValue(obj, start, end, duration) { let startTimestamp = null; const step = (timestamp) => { if (!startTimestamp) startTimestamp = timestamp; const progress = Math.min((timestamp startTimestamp) / duration, 1); // 使用 easeOutQuad 缓动函数让动画更自然 const easeProgress = 1 (1 progress) (1 progress); const currentVal = Math.floor(easeProgress (end start) + start); obj.innerHTML = currentVal; if (progress < 1) { window.requestAnimationFrame(step); } }; window.requestAnimationFrame(step); } // 使用示例 const counter = document.getElementById("counter"); animateValue(counter, 0, 1000, 2000); // 从0滚动到1000,耗时2秒

注:虽然上述示例使用了 requestAnimationFrame,但其逻辑与 setInterval 类似,只是帧率更稳定,若严格使用 setInterval,需注意性能损耗和精度问题。

进阶实现方案:使用 requestAnimationFrame

相比 setInterval,requestAnimationFrame 能更好地与浏览器刷新率同步(通常为 60fps),避免动画卡顿或掉帧,是更推荐的生产环境方案。

优势对比:

滚动数字js怎么实现?js数字滚动插件 第2张

特性 setInterval requestAnimationFrame
执行时机 固定时间间隔,可能错过渲染帧 浏览器下次重绘前执行,同步屏幕刷新率
性能 后台标签页中仍会执行,浪费资源 后台标签页中自动暂停,节省电量
精度 受 JS 事件循环影响,可能有延迟 更高精度,动画更流畅
适用场景 简单逻辑、低性能要求 复杂动画、高性能要求、移动端优化

高级功能扩展

在实际项目中,往往需要处理更复杂的场景,如千分位格式化、小数支持、触发条件等。

  1. 数值格式化

    在更新 DOM 前,对数字进行千分位处理(如 1,000)或保留小数位,可使用 Intl.NumberFormat 或正则表达式实现。

  2. 触发滚动

    通常希望数字在元素进入视口时才启动动画,而非页面加载时,可结合 IntersectionObserver API 实现:

    const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const el = entry.target; const target = parseInt(el.dataset.target); animateValue(el, 0, target, 2000); observer.unobserve(el); // 只触发一次 } }); }); observer.observe(document.getElementById('counter'));
  3. 支持小数与负数

    基础算法需修改为支持浮点数运算,并在格式化时保留指定小数位。

常见问题与解答

问题 1:为什么我的滚动数字动画在低端设备上会出现卡顿?

滚动数字js怎么实现?js数字滚动插件 第3张

解答:

卡顿通常是因为动画逻辑阻塞了主线程,或者使用了 setInterval 导致频繁触发重排(Reflow),建议采取以下优化措施:

  • 使用 requestAnimationFrame 替代 setInterval,确保动画与屏幕刷新率同步。
  • 避免在动画回调中操作复杂的 DOM 属性,仅更新 textContent 或 innerText。
  • 如果数值变化范围极大,可简化计算逻辑,或使用 Web Worker 在后台线程计算(适用于极复杂场景)。
  • 检查是否触发了布局重排,确保只修改样式或文本内容,不修改影响布局的属性(如 width、height)。

问题 2:如何实现数字滚动到目标值后,再次点击按钮重新从 0 开始滚动?

解答:

需要确保动画函数是可重入的(Reentrant),关键在于正确管理定时器或动画帧的引用,并在每次启动新动画前清除旧的动画状态。

具体实现步骤:

  1. 在 animateValue 函数内部,使用 window.requestAnimationFrame 时,无需手动清除,因为每次调用都会创建新的动画帧循环。
  2. 但如果使用 setInterval,必须在全局或闭包中保存 intervalId,并在每次启动新动画前调用 clearInterval(intervalId)。
  3. 重置 DOM 内容:在启动新动画前,将元素内容重置为起始值(如 0)。
  4. 绑定点击事件:在按钮的 click 事件中,先重置数值,再调用 animateValue。

示例代码片段:

let currentAnimationId = null; function resetAndAnimate(el, start, end, duration) { // 如果有正在进行的动画,取消它 if (currentAnimationId) { cancelAnimationFrame(currentAnimationId); } el.innerHTML = start; // 重置显示 animateValue(el, start, end, duration); } // 在 animateValue 内部,将 requestAnimationFrame 的返回值赋给 currentAnimationId // 以便在需要时取消

0