
本文介绍如何将 Odometer.js 计数器改造为支持鼠标悬停即重新播放滚动动画的效果,通过动态重置初始值并触发 update() 方法,解决原生只执行一次动画的问题。
本文介绍如何将 odometer.js 计数器改造为支持鼠标悬停即重新播放滚动动画的效果,通过动态重置初始值并触发 `update()` 方法,解决原生只执行一次动画的问题。
要让 Odometer 数字在每次鼠标悬停时都“重新滚动”,关键在于:每次悬停都需创建一个新的 Odometer 实例(或复用并重置),并先跳转到一个中间随机值,再更新为目标值。这是因为 Odometer 仅在当前值 ≠ 目标值时触发动画;若直接 update(targetValue) 而当前显示已是目标值,则无任何视觉变化。
以下是一个完整、可靠且可复用的实现方案:
✅ 正确初始化与悬停逻辑
首先,避免在 $(document).ready() 中嵌套 window.onload —— 此时 load 事件早已触发,会导致初始化失败。应统一使用 DOM 就绪后立即初始化,并为每个 .odometer 元素绑定 mouseenter 事件(推荐用 mouseenter 替代 hover,避免重复触发):
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function triggerOdometerRoll(element) {
const targetValue = parseInt(element.getAttribute('data-value'), 10);
// 确保 element 已挂载 Odometer 实例(或创建新实例)
let odometer = element.odometerInstance;
if (!odometer) {
// 首次初始化:创建实例但暂不启动动画
odometer = new Odometer({
el: element,
value: 0,
format: '(,ddd)',
duration: 1800,
theme: 'train-station'
});
element.odometerInstance = odometer;
}
// 关键步骤:先设为随机中间值(确保≠target),再更新为目标值
const randomIntermediate = getRandomInt(
Math.max(0, targetValue - 20),
targetValue + 20
);
odometer.update(randomIntermediate);
// 短暂延迟后更新至目标值,确保两次 update 均被识别
setTimeout(() => odometer.update(targetValue), 30);
}
// DOM 加载完成后初始化所有 odometer 并绑定悬停
$(document).ready(function() {
const $odometers = $('.odometer');
// 初始静默加载(不触发动画):设为 0
$odometers.each(function() {
const $el = $(this);
const target = parseInt($el.data('value'), 10);
$el.text('0'); // 清空初始占位符
});
// 绑定悬停事件(使用 mouseenter 防止频繁触发)
$odometers.on('mouseenter', function() {
triggerOdometerRoll(this);
});
// 可选:页面加载完成时自动播放一次(模拟首次进入效果)
setTimeout(() => {
$odometers.each(function() {
triggerOdometerRoll(this);
});
}, 300);
});
? 注意事项与优化建议
- 性能考量:避免在 mouseenter 中反复 new Odometer()。本方案采用「实例缓存」(element.odometerInstance),每个元素仅初始化一次,大幅提升响应速度。
- 动画节奏:将 duration 从 18000ms(18秒)调整为 1800ms(1.8秒)更符合悬停交互预期;如需更轻快效果,可进一步降至 1000–1200。
- 样式兼容性:务必引入 Odometer 官方 CSS 主题(如 odometer-theme-train-station.css),否则数字切换可能无过渡效果。
- 无障碍友好:悬停动画非必需功能,确保静态数值(data-value)仍可通过 HTML 正常读取,不影响屏幕阅读器体验。
✅ 完整依赖引用(HTML head 中)
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/odometer.js/0.4.7/themes/odometer-theme-train-station.css"><script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/odometer.js/0.4.7/odometer.min.js"></script>
通过以上实现,每个 .odometer 元素均可独立响应悬停,流畅滚动至目标值,兼顾性能、可维护性与用户体验。










