【问题标题】:How can I implement a countup that stops at all counters simultaneously?如何实现同时在所有计数器处停止的计数?
【发布时间】:2022-01-15 05:15:12
【问题描述】:

我已经实现了一个动画到数据目标的计数。如何调整计数器以使所有计数器同时停止?

或者有没有更好的方法来实现这个?

function animationEffect(){

const counters = document.querySelectorAll('.counter');
const speed = 2000;

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;
          }
      }
      updateCount();
  });
}
<div class="counter" data-target="299" onmouseover="animationEffect()">0</div>
<div class="counter" data-target="1299" onmouseover="animationEffect()">0</div>
<div class="counter" data-target="99" onmouseover="animationEffect()">0</div>

【问题讨论】:

  • yo.. 已为您的问题提供了答案,您现在可以查看:D

标签: javascript animation


【解决方案1】:

更新答案

看起来我误解了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>

分配变量countersspeed 后,我们可以定义一个数组来保存间隔的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,您可以轻松实现它。

【讨论】:

  • 问题是让单独的计数器同时到达它们的 NUMBER 个目的地;-;
  • Sill,setTimeout 是实现此功能的糟糕方法。我将更新我的答案。
  • @TheBombSquad 我更新了答案
  • 我刚看到.. 它有效
【解决方案2】:

我不知道它是否有帮助,但是当我更改此行时我得到了它: const inc = target / speed;

对此: const inc = 1/(speed / target);

并摆脱Math.ceil(),因为这个const speed = 2000;实际上是一个步骤,它创建了浮动值。也许尝试更小的speed

编辑 稍微调整一下,我们就有了一个没有浮动值的平滑答案>:D

function animationEffect(){

if(animationEffect.called){return null}
animationEffect.called=true
//these 2 lines prevent this function from being called multiple times to prevent overlapping
//the code works without these lines though so remove them if u don't want them

const counters = document.querySelectorAll('.counter');
const incrementConstant = 2; //the higher the number the faster the count rate, the lower the number the slower the count rate
const speed = 2000;

counters.forEach((counter) => {
      const updateCount = () => {
          const target = + counter.getAttribute('data-target');
          counter.storedValue=counter.storedValue||counter.innerText-0; //saving a custom value in the element(the floating value)
          const count = + counter.storedValue; //accessing custom value(and rounding it)
          const inc = incrementConstant/(speed / target); //the math thanks to @Tidus
          if(count < target) {
              counter.storedValue=count+inc
              counter.innerText = Math.round(counter.storedValue);
              setTimeout(updateCount, 1);
          } else {
              counter.innerText = target;
          }
      }
      updateCount();
  });
}
<div class="counter" data-target="299" onmouseover="animationEffect()">0</div>
<div class="counter" data-target="1299" onmouseover="animationEffect()">0</div>
<div class="counter" data-target="99" onmouseover="animationEffect()">0</div>

【讨论】:

  • 它会创建浮动值,但它会执行 OP 想要的操作(让它们同时结束):D
  • 感谢您所说的,我正在附加工作解决方案:D
猜你喜欢
  • 2016-04-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-11
相关资源
最近更新 更多