0

The problem with the code below is that the Counters do not stop at the same time. Is there any way to adjust the duration of the Counters? As far as I know, by using animate function JQuery it is possible to adjust the duration, but I wonder how to combine that with Intersection Observer in order to run animated numbers just as they become visible.

const counterElements = document.querySelectorAll(".count");

// Counters
function counter(target, start, stop) {
  target.innerText = 0.1;
  const counterInterval = setInterval(() => {
    start += 0.1;
    const valueConverted = (Math.round(start * 100) / 100).toFixed(1);
    target.innerText = valueConverted;
    if (valueConverted == stop) {
      clearInterval(counterInterval);
    }
  }, 30);
}

function obCallBack(entries) {
  entries.forEach((entry) => {
    const { target } = entry;
    const stopValue = target.innerText;
    const startValue = 0;
    if (!entry.isIntersecting) return;
    counter(target, startValue, stopValue);
    counterObserver.unobserve(target);
  });
}

const counterObserver = new IntersectionObserver(obCallBack, { threshold: 1 });
counterElements.forEach((counterElem) => counterObserver.observe(counterElem));
.emptyspace{
  height:400px;
  }
<div class="emptyspace"></div>
<p class="count">5.2</p>
<p class="count">50.9</p>
</div>

1 Answers1

0

You should use a ratio rather than a fixed number.

const speed = 100;
const inc = Number(stop / speed);

const counterElements = document.querySelectorAll(".count");
const speed = 100; // the lower the slower

// Counters
function counter(target, start, stop) {
  target.innerText = 0.1;
  const counterInterval = setInterval(() => {
    const inc = Number(stop / speed);
    start += inc;
    const valueConverted = (Math.round(start * 100) / 100).toFixed(1);
    target.innerText = valueConverted;
    if (valueConverted == stop) {
      clearInterval(counterInterval);
    }
  }, 30);
}

function obCallBack(entries) {
  entries.forEach((entry) => {
    const { target } = entry;
    const stopValue = target.innerText;
    const startValue = 0;
    if (!entry.isIntersecting) return;
    counter(target, startValue, stopValue);
    counterObserver.unobserve(target);
  });
}

const counterObserver = new IntersectionObserver(obCallBack, { threshold: 1 });
counterElements.forEach((counterElem) => counterObserver.observe(counterElem));
.emptyspace{
  height:400px;
  }
<div class="emptyspace"></div>
<p class="count">5.2</p>
<p class="count">50.9</p>
</div>
DanOne
  • 1
  • This fails to stop counting if the original/target numbers are integers. How can this be adjusted to work with both decimals and integers? – Sandwich Jul 23 '23 at 08:40