Javascript 用各种 setTimeout 来对抗

Javascript counter up with various setTimeout

我从各种来源创建了我能找到的最简单的 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>

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

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

像这样 setTimeout(updateCount150, 100); setTimeout(updateCount300, 50);

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

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

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>

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

这是一个基本示例,可能还需要一些工作。我还建议使用 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>