A simple counter animation

Hey guys,

Here is a simple javascript counter animation that I created...

  • Triggered if counters are inview (scrolling down otherwise)
  • Optional abriviation on the numbers
  • Optional extra symbol (+, $, % etc..)
  • Optional decimal places ( 0, 1, 2, 3 etc)
  • Optional starting number

Here is the html (ignore the bootstrap styling if you need to set your own classes):

<div class="py-4 container-fluid">
    <div id="counterElements" class="row row-cols-1 row-cols-md-4 row-cols-sm-2 border-top border-primary border-2 pt-3">
        <div class="col p-3 text-center txt-shadow-black-bold">
            <i class="fas fa-layer-group fa-3x text-body txt-shadow-secondary-wide w-100"></i>
            <p class="counter text-center h1 text-primary fw-bold mb-0 lh-base w-auto d-inline" data-count="1254738" data-decimals="2" data-abr="">0</p>
            <span class="d-inline-block h4 text-primary fw-light"></span>
            <p class="text-center mb-0 small text-body">POSTS</p>
        </div>
        <div class="col p-3 text-center txt-shadow-black-bold">
            <i class="fas fa-tasks fa-3x text-body txt-shadow-secondary-wide w-100"></i>
            <p class="counter text-center h1 text-primary fw-bold mb-0 lh-base w-auto d-inline" data-count="285" data-decimals="0" data-abr="">0</p>
            <span class="d-inline-block h4 text-primary fw-light">+</span>
            <p class="text-center mb-0 small text-body">TASKS</p>
        </div>
        <div class="col p-3 text-center txt-shadow-black-bold">
            <i class="far fa-id-card fa-3x text-body txt-shadow-secondary-wide w-100"></i>
            <p class="counter text-center h1 text-primary fw-bold mb-0 lh-base w-auto d-inline" data-count="24592" data-decimals="2" data-abr="">0</p>
            <span class="d-inline-block h4 text-primary fw-light"></span>
            <p class="text-center mb-0 small text-body">MEMBERS</p>
        </div>
        <div class="col p-3 text-center txt-shadow-black-bold">
            <i class="far fa-calendar-check fa-3x text-body txt-shadow-secondary-wide w-100"></i>
            <p class="counter text-center h1 text-primary fw-bold mb-0 lh-base w-auto d-inline" data-count="1296" data-decimals="0" data-abr="no">0</p>
            <span class="d-inline-block h4 text-primary fw-light">%</span>
            <p class="text-center mb-0 small text-body">SUCCESS</p>
        </div>
    </div>
</div>

and here is the javascript:

$(document).ready(function () {
    const counts = document.querySelectorAll('.counter');

    var element = document.getElementById('counterElements');
    var elementHeight = element.clientHeight;

    // check if counters are already inView on page load
    if (inView()) {
        // element is in view, start animation
        startCountAnimation();
    }

    // listen for scroll event and call animate function
    document.addEventListener('scroll', animate);

    // check if element is in view
    function inView() {
        // get window height
        var windowHeight = window.innerHeight;
        // get number of pixels that the document is scrolled
        var scrollY = window.scrollY || window.pageYOffset;

        // get current scroll position (distance from the top of the page to the bottom of the current viewport)
        var scrollPosition = scrollY + windowHeight;
        // get element position (distance from the top of the page to the bottom of the element)
        var elementPosition = element.getBoundingClientRect().top + scrollY + elementHeight;

        // is scroll position greater than element position? (is element in view?)
        if (scrollPosition > elementPosition) {
            document.removeEventListener('scroll', animate);
            return true;
        }

        return false;
    }

    // animate element when it is in view
    function animate() {
        // is element in view?
        if (inView()) {
            // element is in view, start animation
            startCountAnimation();
        }
    }

    function startCountAnimation() {
        counts.forEach((counter) => {

            function upDate() {
                // get the data attributes for .counters elements
                const countAbr = counter.getAttribute('data-abr') == "no" ? false : true;
                const countNumber = Number(counter.getAttribute('data-count'));
                const countDecimals = (Number(counter.getAttribute('data-decimals')) ? Number(counter.getAttribute('data-decimals')) : 0);
                // get the starting number for the counter
                const count = Number(counter.innerText);
                // set the speed ( the higher value the slower animation)
                const speed = 60;
                // set the suffix symbol for thousands and millions
                let smbl = ((countNumber < 1000 && countAbr) ? "" : (countNumber < 1000000 && countAbr) ? "K" : (countNumber >= 1000000 && countAbr) ? "M" : "");
                // set the multiplier for calculating the new number
                let sess = ((countNumber < 1000 && countAbr) ? 1 : (countNumber < 1000000 && countAbr) ? 1000 : (countNumber >= 1000000 && countAbr) ? 1000000 : 1);
                // set the symbol to .counter style properties
                counter.style.setProperty("--symbl", '"' + smbl + '"');
                // calculating the target/end number of the .counter animation
                target = (countNumber / sess);

                // set the increasing step 
                const inc = (target / speed);

                // if the current number is lower than the target then set the new number and set the timer
                if (count < target) {
                    counter.innerText = Number((inc + count)).toFixed(countDecimals);
                    setTimeout(upDate, speed);
                }
                // else set the exact end number as in the data-count attribute
                else {
                    counter.innerText = target.toFixed(countDecimals);
                }
            }
            upDate();
        })
    }
});
8 Likes

I didn't know you actually coded :face_with_spiral_eyes:

Yikes!
(I know you probably meant a simple counter, just toying around)

2 Likes