【问题标题】:Javascript counter up with various setTimeoutJavascript 计数器与各种 setTimeout
【发布时间】:2021-01-08 18:08:06
【问题描述】:

我从各种来源创建了我能找到的最简单的 js 计数器。如何为每个计数器设置不同的 setTimeout,以便它们同时结束?

更新:我已将下面的正确答案最小化,这是最终代码:

const counters = document.querySelectorAll('.counter');
const duration = 2000; // Finish in 2 seconds
const speed    = 2000;
const state    = {};

const max = Math.max(...[...counters]
  .map(counter => +counter.dataset.target));
const tick = duration / max;

const updateCount = (counter) => {
  const target = +counter.dataset.target;
  const value  = +counter.dataset.value;
  const ratio  = target / max;
  const ms     = Math.ceil(ratio * tick);
  const incr   = target / speed;
  const newVal = value + incr;
 
  if (value < target) {
    counter.innerText = Math.floor(newVal);
    counter.dataset.value = newVal;
    setTimeout(updateCount, ms, counter);
  } else {
    counter.innerText = target;
    counter.dataset.value = target;
  }
};
counters.forEach(updateCount);
<div class="counter" data-key="1" data-value="0" data-target="1000">0</div>
<div class="counter" data-key="2" data-value="0" data-target="2000">0</div>

【问题讨论】:

  • 你不能让两个定时器同时结束,因为 JavaScript 是同步运行的。
  • 如果它们以毫秒为单位的速率不同,那么它们永远不会同时结束。
  • 如果我为每个 div 使用 id 会怎样?

标签: javascript html counter settimeout


【解决方案1】:

每个计时器必须以相对于最大时间的增加速率运行。

这是一个初步的例子,它可能还需要一些工作。我还建议使用dataset 属性来存储值并仅重新呈现值(如果结果是浮点数)。

const results  = document.querySelector('.results');
const counters = document.querySelectorAll('.counter');
const duration = 2000; // Finish in 2 seconds
const speed    = 1000;
const state    = {};

const max = Math.max(...[...counters]
  .map(counter => +counter.dataset.target));
const tick = duration / max;

const updateCount = (counter) => {
  const target = +counter.dataset.target;
  const value  = +counter.dataset.value;
  const ratio  = target / max;
  const ms     = Math.ceil(ratio * tick);
  const incr   = target / speed;
  const newVal = value + incr;
  
  const { dataset: { key }} = counter;
  state[key] = {
    ...state[key],
    ratio, ms, incr, value
  };
  results.textContent = JSON.stringify(state, null, 2);
  
  if (value < target) {
    counter.innerText = Math.floor(newVal);
    counter.dataset.value = newVal;
    setTimeout(updateCount, ms, counter);
  } else {
    counter.innerText = target;
    counter.dataset.value = target;
  }
};

counters.forEach(updateCount);
.results {
  border: thin solid grey;
  padding: 0.25em;
  white-space: pre;
  font-family: monospace;
}
<div class="counter" data-key="1" data-value="0" data-target="1000">0</div>
<div class="counter" data-key="2" data-value="0" data-target="2000">0</div>
<div class="counter" data-key="3" data-value="0" data-target="4000">0</div>

<div class="results"></div>

【讨论】:

  • 看起来不错。我对js不是很了解,但我会尝试玩它并回复你。
  • 我进行了适当的调整并使代码更小。我会将其标记为正确答案!非常感谢!
【解决方案2】:

它是这样工作的,因为您的 setTimeout(updateCount, 100); 在 100 毫秒后运行。因此,当您从 0 计数到 150 并且每 100 毫秒添加一个数字时,它会比计数到 300 快两倍。

您可以同时结束,当您将setTimeout() 更改为在 50 毫秒后运行以计数到 300

类似的东西 setTimeout(updateCount150, 100); setTimeout(updateCount300, 50);

当然,您需要相应地调整这两个功能。

【讨论】:

  • 你是说我必须为每个计数器复制 js?
【解决方案3】:

尝试获取平均值,如下所示:

const counters = document.querySelectorAll('.counter');
const speed = 1;
let avg=0;
counters.forEach(counter => {
   avg+=parseInt(counter.getAttribute('data-target'))
});
avg=avg/counters.length;
counters.forEach(counter => {
  const updateCount = () => {
    const count = +counter.innerText;
    const target = +counter.getAttribute('data-target')
    const inc = counter.getAttribute('data-target') / (speed * avg);

    if (count < target) {

      counter.innerText = count + inc;

      setTimeout(updateCount, 100);
    } else {
      counter.innerText = target;
    }
  };

updateCount();
});
<div class="counter" data-target="150">0</div>
                
<div class="counter" data-target="300">0</div>

【讨论】:

  • 它很好用,但是你怎么能隐藏小数呢?
猜你喜欢
  • 2011-02-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-12
  • 1970-01-01
  • 2010-11-18
相关资源
最近更新 更多