Animated JavaScript Counter-Up with the Intersection Observer API

on in JavaScript DOM
Last modified on

An animated counter is a more engaging way to present this information, as it gradually increases the value of the number from zero to its final value. In this tutorial, we will learn how to use the IntersectionObserver API to create an animated counter that starts when the user scrolls to the counter section on the webpage.

We start by creating a basic HTML structure for the counter and add basic CSS styles to create a basic design. You can add more to match your project styles. Next, we add JavaScript code to create a counter animation that counts up to a specified number over a specified duration using JavaScript.

Finally, we add an IntersectionObserver function to detect when the counter element(s) are in view and trigger the counter animation. This allows the animation to start when the element is scrolled into view, providing a better user experience and optimizing performance.

Step 1: Set up the HTML structure

Let’s start by creating the HTML structure for the counter. We will create a wrapper <div> for each counter value and its associated label. Each counter value will be represented by a <span> element with a class of .counterup.

Note that having different values in each counter element looks better than having the same value, so try to get different numerical values for different aspects of your business.

<div class="counter-item">
    <div class="count-wrapper">
        <h4><span class="counterup">1000</span>+</h4>
        <p>Projects Completed</p>
    </div>
</div>
<div class="counter-item">
    <div class="count-wrapper">
        <h4><span class="counterup">100</span>+</h4>
        <p>Happy Clients</p>
    </div>
</div>

Step 2: Add CSS styles

Now that we have the HTML structure in place, we need to add some CSS styles to make the counters look good. Here’s the CSS code:

.counter-item {
    text-align: center;
}
.count-wrapper {
    padding: 2em;
}
.counter-item h4 {
    font-size: 64px;
    line-height: 1;
    margin: 0;
}
.counter-item p {
    font-size: 18px;
    font-weight: 500;
    margin: 1em 0 0 0;
}

Step 3: Add Intersection Observer to Detect When Elements are in View

The next step is to create an Intersection Observer that will detect when the .counterup elements are in view and trigger the counter animation.

An Intersection Observer is a powerful API that allows you to observe changes in the intersection of a target element with an ancestor element or with a top-level document’s viewport. In this case, we’ll use an Intersection Observer to detect when the .counterup elements are in view and trigger the counter animation.

document.addEventListener('DOMContentLoaded', () => {
    // Create new intersection observer
    const observer = new IntersectionObserver((entries, observer) => {
        entries.forEach(entry => {
            // If the element is in view, start counter animation
            if (entry.isIntersecting) {
                const target = entry.target;
                const finalValue = parseInt(target.textContent, 10);

                let startValue = 0;
                const duration = 4000;
                const startTime = performance.now();

                const animateCounter = (currentTime) => {
                    const elapsedTime = currentTime - startTime;
                    const progress = Math.min(elapsedTime / duration, 1);
                    const currentValue = Math.ceil(progress * finalValue);
                    target.textContent = currentValue;

                    if (progress < 1) {
                        requestAnimationFrame(animateCounter);
                    }
                };

                requestAnimationFrame(animateCounter);
                // Stop observing the element to prevent duplicate animations
                observer.unobserve(target);
            }
        });
    });

    // Observe each .counterup element
    document.querySelectorAll('.counterup').forEach(element => {
        observer.observe(element);
    });
});

This code creates a new IntersectionObserver object and defines a callback function that will be called when the observed elements intersect with the root element. The callback function receives two arguments: entries, which is an array of IntersectionObserverEntry objects, and observer, which is a reference to the observer instance.

Inside the callback function, the code loops through each entry in the entries array and checks if the element is intersecting with the root element by checking the isIntersecting property. If the element is in view, the code starts a counter animation.

Finally, the code stops observing the element by calling observer.unobserve(entry.target) to prevent duplicate animations when the element intersects with the root element multiple times.

Related posts

Leave a Reply

Your email address will not be published. Required fields are marked *