更新答案
看起来我误解了at the same time 的意思,但是像这样使用setTimeout 仍然是一个非常糟糕的做法。这是我的看法:
const counters = Array.from(document.querySelectorAll(".counter"));
const counterValues = [];
const speed = 500;
const updateCount = (counter, target, count, index) => {
const inc = target / speed;
counterValues[index] = count + inc;
if (count < target) {
counter.innerText = Math.floor(counterValues[index]);
} else {
counter.innerText = target;
}
};
counters.forEach((counter, index) => {
counterValues.push(0)
const interval = setInterval(() => {
const target = +counter.getAttribute("data-target");
const count = counterValues[index];
if (target !== count) {
updateCount(counter, target, count, index)
} else {
clearInterval(interval);
}
}, 1)
});
<div class="counter" data-target="32">0</div>
<div class="counter" data-target="3000">0</div>
<div class="counter" data-target="10">0</div>
请查看我的旧答案,了解为什么 setInterval 更适合此问题。
除此之外,这里是这个 sn-p 中发生的事情:
我定义了一个名为counterValues 的数组,它将以浮点格式保存计数值。在您的示例中,当您存储要在以后再次用于计算的上限数字时,您没有进行正确的计算。
如果我没记错的话,您的计数器之一必须增加 0.145,而您每次都将其增加 1。顺便说一句,地板是正确的方法,因为它在真正到达目标之前不会到达目标。如果目标是10,但你的计数器是9.5,它会在你的代码中写成10,虽然它还没有。
updateCount 几乎是相同的功能。它现在使用floor。它使用先前的浮点值更新计数器的数量,然后在写入 DOM 时,它使用底值。
对于每个计数器,它会添加一个间隔,该间隔将更新计数器并在计数器达到目标值时自行取消。
为了简单起见,我使用了共享状态和索引计算。
旧答案
如果您将this 代码粘贴到代码顶部并运行,当您登录window.activeTimers 时,您将看到定义了数百个计时器。这是因为每次调用updateCount 时,您都在为updateCount 设置一个新计时器。尽管您的animationEffect 函数就像您的程序的主要函数,但如果您每次将鼠标悬停在计数器上时调用它,它将设置新的计时器,这意味着每次都更快地更新您的计数器。总而言之,你现在完全没有控制权。
对于定期通话,您应该使用setInterval。它需要一个函数和一个延迟参数(还有可选参数。您可以查看文档)。它repeatedly calls a function or executes a code snippet, with a fixed time delay between each call (From Mozilla docs)。它还返回一个间隔 ID,以便您以后可以取消它(这意味着我们可以控制它)。
因此,在您的情况下,您应该做的第一件事就是摆脱对 animationEffect 的 onmouseover 调用,并添加一个按钮来停止执行 updateCounters。
<div class="counter" data-target="299">0</div>
<div class="counter" data-target="1299">0</div>
<div class="counter" data-target="99">0</div>
<button id="stop">Stop</button>
分配变量counters 和speed 后,我们可以定义一个数组来保存间隔的ID,以便以后取消它们。
const counters = document.querySelectorAll(".counter");
const speed = 2000;
const cancelButton = document.getElementById('stop')
const countIntervals = [];
cancelButton.addEventListener('click', () => {
countIntervals.forEach(interval => clearInterval(interval))
})
如您所见,我为我们的按钮定义了一个事件监听器。当您单击它时,它将迭代 countIntervals 中的间隔 ID 并清除这些间隔。为简单起见,我没有实现诸如暂停和重置之类的功能,并且尽量不要过多地更改您的代码。您可以稍后进行实验。
现在您应该做的第一件事是在 if 语句中注释或删除 setTimeout 行。然后我们将返回的区间ID推送到我们的数组countIntervals:
counters.forEach((counter) => {
const updateCount = () => {
const target = +counter.getAttribute("data-target");
const count = +counter.innerText;
const inc = target / speed;
if (count < target) {
counter.innerText = Math.ceil(count + inc);
// setTimeout(updateCount, 1);
} else {
counter.innerText = target;
}
};
countIntervals.push(setInterval(updateCount, 10))
});
现在,一旦您点击Stop 按钮,您的计数器就会停止。我忽略了速度功能,但如果您了解setInterval,您可以轻松实现它。