Saya membuat dari berbagai sumber penghitung js paling sederhana yang bisa saya temukan. Bagaimana saya bisa membuat setTimeout berbeda untuk setiap penghitung sehingga keduanya berakhir pada waktu yang sama?

PEMBARUAN: Saya telah meminimalkan jawaban yang benar di bawah ini dan ini adalah kode terakhir:

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>
0
NickDimou 8 Januari 2021, 21:08

3 jawaban

Jawaban Terbaik

Setiap timer harus berjalan pada tingkat yang meningkat relatif terhadap waktu maksimum.

Ini adalah contoh yang belum sempurna, dan mungkin masih perlu beberapa pekerjaan. Saya juga merekomendasikan menggunakan properti dataset untuk menyimpan nilai dan hanya merender ulang nilai (jika hasilnya adalah angka floating-point).

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>
1
Mr. Polywhirl 8 Januari 2021, 19:28

Ini berfungsi seperti ini karena setTimeout(updateCount, 100); Anda berjalan setelah 100 milidetik. Jadi, ketika Anda menghitung dari 0 hingga 150 dan Anda menambahkan angka sekali per 100 md, itu akan menjadi dua kali lebih cepat daripada menghitung hingga 300.

Anda dapat mengakhirinya pada saat yang sama, ketika Anda mengubah setTimeout() untuk dijalankan setelah 50 milidetik untuk menghitung hingga 300

Sesuatu seperti ini setTimeout(updateCount150, 100); setTimeout(updateCount300, 50);

Tentu saja Anda perlu menyesuaikan kedua fungsi tersebut.

1
mrblue 8 Januari 2021, 18:24

Coba dapatkan rata-rata, seperti ini:

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>
1
Elvis 8 Januari 2021, 19:25