
本文介绍使用原生 html、css 和 javascript(兼容 jquery)实现“滚动到指定区域时数字从 0 开始逐帧递增至目标值”的交互动画,支持精准触发、防重复执行,并对比说明是否需引入 react 等框架。
本文介绍使用原生 html、css 和 javascript(兼容 jquery)实现“滚动到指定区域时数字从 0 开始逐帧递增至目标值”的交互动画,支持精准触发、防重复执行,并对比说明是否需引入 react 等框架。
在网页开发中,常需在用户滚动至统计模块(如“完成项目数:500”“经验年限:10”)时,让数字以平滑动画形式从 0 动态增长至设定值——这种“滚动触发动画”能显著提升视觉表现力与用户体验。以下为轻量、可靠且无需额外框架的实现方案。
✅ 核心逻辑说明
动画仅在目标区块(如 #counter)进入视口时触发,且只执行一次,避免重复滚动导致数字重播。关键点包括:
- 使用
$(window).scrollTop()与$('#counter').offset().top计算元素距顶部距离; - 引入标志变量
a(初始为0)控制动画仅运行一次; - 利用 jQuery 的
.animate()方法驱动数字变化,配合step回调实时更新 DOM 文本; - 支持整数与百分比(如
100后可加%符号,代码中可扩展格式化逻辑)。
? 完整可运行示例
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>滚动数字动画</title><script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script><style>
.text-center { text-align: center; }
.statistics {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 20px;
margin: 60px 20px;
}
.stastic {
text-align: center;
background-color: #fff8e1;
padding: 24px 16px;
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0,0,0,0.08);
}
.stastic h4 {
font-size: 28px;
font-weight: 700;
color: #1976d2;
margin: 0 0 12px;
}
.stastic p {
margin: 0;
color: #666;
font-size: 16px;
}
</style><p class="text-center">↓ 请向下滚动以触发数字动画 ↓</p>
<!-- 大量占位内容,确保可滚动 -->
<div style="height: 120vh; background: #f5f5f5;"></div>
<section id="counter" class="statistics"><div class="stastic">
<h4 class="counter">500</h4>
<p>Projects Done</p>
</div>
<div class="stastic">
<h4 class="counter">10</h4>
<p>Years Experience</p>
</div>
<div class="stastic">
<h4 class="counter">100</h4>
<p>Success Rate</p>
</div>
</section><div style="height: 100vh; background: #e8f5e9;"></div>
<script>
let hasAnimated = false;
$(window).on('scroll', function () {
const $counterSection = $('#counter');
if (!$counterSection.length || hasAnimated) return;
const sectionTop = $counterSection.offset().top;
const windowHeight = $(window).height();
const scrollTop = $(this).scrollTop();
// 当 section 上边缘进入视口底部时触发(预留缓冲)
if (scrollTop + windowHeight >= sectionTop + 50) {
$('.counter').each(function () {
const $el = $(this);
const target = parseInt($el.text(), 10) || 0;
$({ count: 0 }).animate(
{ count: target },
{
duration: 2000,
easing: 'swing',
step: (now) => $el.text(Math.ceil(now)),
complete: () => $el.text(target) // 确保最终值精确
}
);
});
hasAnimated = true; // 锁定,防止重复执行
}
});
</script>
⚠️ 注意事项与优化建议
-
性能考量:
scroll事件高频触发,已通过hasAnimated标志和早期返回优化;生产环境建议添加throttle(节流),例如使用 Lodash 的_.throttle或简易实现; -
无障碍支持:动画完成后应保留最终数值,屏幕阅读器可正常读取;若需语义强化,可为
.counter添加aria-live="polite"; -
纯 JS 替代方案:如不依赖 jQuery,可用
IntersectionObserver实现更现代、高性能的进入视口检测(推荐用于新项目):const observer = new IntersectionObserver((entries) => { if (entries[0].isIntersecting && !hasAnimated) { // 执行动画逻辑... hasAnimated = true; observer.unobserve(entries[0].target); } }, { threshold: 0.1 }); observer.observe(document.getElementById('counter')); -
关于 React 等框架:对于简单静态页面,完全无需 React —— 原生方案体积小、加载快、维护成本低;仅当数字需响应状态变化(如 API 实时更新)、或集成复杂交互组件时,才建议使用 React/Vue 配合
useEffect+IntersectionObserver实现。
✅ 总结
该方案兼顾兼容性、可读性与实用性,一行 CSS Grid 布局 + 精炼的 jQuery 动画逻辑即可交付专业效果。优先采用原生技术栈,按需渐进增强,才是稳健的前端实践之道。










