如何实现元素进入视口时触发统计计数器动画函数运行
实现方案
问题说明
你现有代码的核心问题有两个:
- 计数动画逻辑在页面加载时直接执行,和视口判断逻辑完全独立
- jQuery 本身没有内置
isInViewport()方法,直接调用会报错
完整实现代码
JavaScript 代码
// 先实现jQuery isInViewport 扩展方法 $.fn.isInViewport = function() { const elementTop = $(this).offset().top; const elementBottom = elementTop + $(this).outerHeight(); const viewportTop = $(window).scrollTop(); const viewportBottom = viewportTop + $(window).height(); // 只要元素有部分出现在视口内就返回true return elementBottom > viewportTop && elementTop < viewportBottom; }; // 防抖函数,避免滚动事件频繁触发 function debounce(func, wait) { let timeout; return function() { const context = this; const args = arguments; clearTimeout(timeout); timeout = setTimeout(() => func.apply(context, args), wait); }; } // 封装计数动画逻辑 function runCountAnimation() { // 加标记,避免重复执行动画 if ($('.numbers-inner').data('animated')) return; $('.magPub, .pplHired, .yrsExp').each(function() { $(this).prop('Counter', 0).animate({ Counter: $(this).text() }, { duration: 1500, easing: 'swing', step: function(now) { $(this).text(Math.ceil(now)); } }); }); // 标记已执行过动画 $('.numbers-inner').data('animated', true); } // 页面加载先判断一次,避免初始就在视口内不触发 $(document).ready(function() { if ($('.numbers-inner').isInViewport()) { runCountAnimation(); } }); // 滚动监听加防抖,100ms触发一次即可 $(window).scroll(debounce(function() { if ($('.numbers-inner').isInViewport()) { runCountAnimation(); } }, 100));
原有HTML代码无需修改
逻辑说明
- 先扩展
isInViewport方法实现元素视口判断,原理是对比元素边界和视口边界的坐标 - 加防抖逻辑优化滚动事件性能,避免频繁触发判断
- 给父容器加
data-animated标记,保证动画只执行一次,不会滚动到就反复触发 - 页面加载完成后先判断一次容器位置,如果初始就出现在视口内直接执行动画,不需要等滚动触发
如果需要实现容器离开视口就重置计数、进入重新动画的效果,把标记清除的逻辑放到else分支即可:
$(window).scroll(debounce(function() { if ($('.numbers-inner').isInViewport()) { runCountAnimation(); } else { // 离开视口重置标记,下次进入重新动画 $('.numbers-inner').data('animated', false); // 可选:重置数字为初始值 $('.magPub').text(33708); $('.pplHired').text(247); $('.yrsExp').text(17); } }, 100));
内容的提问来源于stack exchange,提问作者Smucek
相关产品推荐
相关产品推荐

