0

I want to implement a counter that counts in the given time duration. If I want to count 100 in one second, then the gap between each count should be 10ms as a result by the time it gets counted 100 it becomes 1 second. For 1000 the gap between each count should be 1ms.

I've tried using setInterval and changing the delay of the counter dynamically based on the number I want to count, but there's a problem with setInterval, it is not working as expected. Other than using setInterval how can I implement this counter in JavaScript. Thank you in advance.

const btn = document.querySelector('button');
const counter1 = document.querySelector('.counter1');
const counter2 = document.querySelector('.counter2');

btn.addEventListener('click', ()=> {
    increment(100, counter1);
  increment(1000, counter2);
})

const increment = (n, counter) => {
  let m = 0;
  let time = 1000 / n;
  setInterval(() => {
    if (m < n) {
      m += 1;
      counter.textContent = m;
    } else clearInterval(this);
  }, time);
  
};
.counter-container {
 display: flex;
}

.counter {
  margin: 10px;
  text-align: center;
}
button {
  margin-left: 80px;
};
<div class="counter-container">
<div class="counter">
  <h3>Counter1</h3>
  <p class="counter1">100</p>
</div>
<div class="counter">
  <h3>Counter2</h3>
  <p class="counter2">1000</p>
</div>
</div>

<button>start</button>
hashBender
  • 76
  • 8
  • Might want to read this post: https://stackoverflow.com/questions/32307483/in-javascript-how-to-create-an-accurate-timer-with-milliseconds. Basically, you're not going to get a counter that works super well at the ms level. But if you were to do, say, n=10 and n=100, those would finish at the same time. – Nick Jan 02 '21 at 06:44

1 Answers1

1

setInterval() and setTimeout() are not actually reliable when it comes to timing. I would use a combination of those with javascript's Date.now(), which returns the number of milliseconds since the browser's/os's epoch. So you could do something like this:w

const btn = document.querySelector('button');
const counter1 = document.querySelector('.counter1');
const counter2 = document.querySelector('.counter2');

btn.addEventListener('click', ()=> {
    increment(100, counter1);
  increment(1000, counter2);
})

const increment = (n, counter, length=1000) => {
  let start = Date.now()
  let end = start + length
  let interval = length / n;
  setInterval(() => {
    time = Date.now()
    if (time < end) {
      count = (time - start) / interval
      counter.textContent = count;
    } else {
      counter.textContent = n;
      clearInterval(this)
    };
  }, interval/2);
  
};
.counter-container {
 display: flex;
}

.counter {
  margin: 10px;
  text-align: center;
}
button {
  margin-left: 80px;
};
<div class="counter-container">
<div class="counter">
  <h3>Counter1</h3>
  <p class="counter1">100</p>
</div>
<div class="counter">
  <h3>Counter2</h3>
  <p class="counter2">1000</p>
</div>
</div>

<button>start</button>
  • When the interval(length/n) is other than 1, the count values will be a fraction as well. Also when setInterval is not accurate adding the same interval to it, will give unstable counts. You can see the instability by changing to different numbers. – hashBender Jan 02 '21 at 07:24
  • We can have a floor value for this to avoid instability in changing number: `Math.floor((time - start) / interval)` – hashBender Jan 03 '21 at 06:49