JavaScript滚动触发数字计数器实现问题求助
滚动触发数字计数器解决方案
你的代码存在两个核心问题,导致滚动触发功能失效:
- 当前代码在页面加载完成后直接执行计数逻辑,没有绑定滚动事件来触发;
- 计数逻辑的else分支里存在错误:
count.innerText = target中的count是数值类型,不能调用innerText,应该改为counter.innerText = target。
下面是修复后的完整代码,实现滚动到计数器区域时才开始数字动画,且每个计数器只触发一次:
HTML 代码
<link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;900&display=swap" rel="stylesheet"> <div class="container"> <div class="heading"> Resultater </div> <!-- 给计数器容器加个id方便获取 --> <div id="counter-container" class="counter-container"> <div class="counter"> <img src="#" alt="timer" class="icon"> <h3 data-target="200" class="count">0</h3> <h6>Text 1</h6> </div> <div class="counter"> <img src="#" class="icon"> <h3 data-target="217" class="count">0</h3> <h6>Text 2</h6> </div> <div class="counter"> <img src="#" alt="night" class="icon"> <h3 data-target="511" class="count">0</h3> <h6>Text 3</h6> </div> </div> <!-- 加一段空白让页面能滚动 --> <div style="height: 1000px;"></div> </div>
CSS 代码
* { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: "verdana", sans-serif; background: #ffffff; } .container { width: 80%; margin: auto; } .heading { text-align: center; font-size: 3.5rem; font-weight: bold; padding: 5rem 0; color: #505050; } .counter-container { display: flex; justify-content: space-around; align-items: center; } .counter { text-align: center; } .counter h3 { padding: 0.5rem 0; font-size: 2.5rem; font-weight: 800; } .counter h6 { font-size: 2rem; padding-bottom: 1rem; } .icon { height: 5rem; width: auto; }
JavaScript 代码
const counters = document.querySelectorAll(".count"); const speed = 200; const counterContainer = document.getElementById("counter-container"); // 标记是否已经触发过计数,避免重复执行 let hasTriggered = false; // 判断元素是否进入视口的函数 function isInViewport(element) { const rect = element.getBoundingClientRect(); return ( rect.top <= (window.innerHeight || document.documentElement.clientHeight) && rect.bottom >= 0 ); } // 计数逻辑 function updateCount(counter) { const target = parseInt(counter.getAttribute("data-target")); const count = parseInt(counter.innerText); const increment = Math.trunc(target / speed); if (count < target) { counter.innerText = count + increment; setTimeout(() => updateCount(counter), 1); } else { counter.innerText = target; } } // 滚动监听事件 window.addEventListener("scroll", () => { if (isInViewport(counterContainer) && !hasTriggered) { counters.forEach(counter => { updateCount(counter); }); hasTriggered = true; } }); // 页面加载时先检查一次(如果计数器一开始就在视口内) window.addEventListener("load", () => { if (isInViewport(counterContainer) && !hasTriggered) { counters.forEach(counter => { updateCount(counter); }); hasTriggered = true; } });
关键逻辑说明
- 视口检测函数:
isInViewport通过元素的位置和窗口高度判断元素是否进入用户视野; - 滚动监听:每次滚动时检查计数器容器是否在视口内,且未触发过计数,满足条件则启动所有计数器的动画;
- 防重复触发:用
hasTriggered变量标记,确保每个计数器只执行一次动画; - 页面加载检查:避免页面加载时计数器已经在视口内但未触发的情况。
内容的提问来源于stack exchange,提问作者M. El-Set
相关产品推荐
相关产品推荐

