You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript滚动触发数字计数器实现问题求助

滚动触发数字计数器解决方案

你的代码存在两个核心问题,导致滚动触发功能失效:

  1. 当前代码在页面加载完成后直接执行计数逻辑,没有绑定滚动事件来触发;
  2. 计数逻辑的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;
  }
});

关键逻辑说明

  1. 视口检测函数:isInViewport 通过元素的位置和窗口高度判断元素是否进入用户视野;
  2. 滚动监听:每次滚动时检查计数器容器是否在视口内,且未触发过计数,满足条件则启动所有计数器的动画;
  3. 防重复触发:用hasTriggered变量标记,确保每个计数器只执行一次动画;
  4. 页面加载检查:避免页面加载时计数器已经在视口内但未触发的情况。

内容的提问来源于stack exchange,提问作者M. El-Set

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 07:21:34