Animated Counter
Tags: javascript, webdev, css, html
Published: 2022-01-09 17:49:50

A counter could be a nice to have to make your website look nicer. 

### Let's start with our HMTL

We are going to use data-sets to set the desired number to count to and the duration it should take animating.

`<h2 data-count-to="25" data-duration="3500" class="counter">0</h2>`

We will dynamically parse this data-set values with Javascript

### Javascript part

Let's start by creating 3 helper functions to parse the data-sets and update the counter text (the number).

``` 
const getNumber = (counter) => {
  return parseFloat(counter.dataset.countTo);
};

const getSpeed = (counter) => {
  return parseFloat(counter.dataset.duration);
};

const updateTex = (counter, text) => {
  counter.textContent = text;
};
```
#### Here is where the **magic** happens

Now let's build our animation function which will take 3 parameters: **counter**, **countTo** and **duration**.

This function should run until our count reaches the desired number and should take our desired duration time. 

For this we need to create two variables **startTime** initialised to null and **currentTime** initialised to the current date (time)

``` 
  let startTime = null;

  let currentTime = Date.now();
```

Next, we will create our step function.

``` 
 const step = (currentTime) => {
    if (!startTime) {
      startTime = currentTime;
    }

    const progress = Math.min((currentTime - startTime) / duration, 1);

    const currentNum = Math.floor(progress * countTo);

    updateTex(counter, currentNum);

    if (progress < 1) {
      window.requestAnimationFrame(step);
    } else {
      window.cancelAnimationFrame(window.requestAnimationFrame(step));
    }
  };
```

**requestAnimationFrame** will call our function around 60 times per second this way our animation will look smoother. 
 
Worth mentioning that **Math.min** returns the lowest-valued number passed into it. In our case this will run until it gets to 1.

When **progress** reaches 1 means we arrived at our desired number (`progress * countTo`) and we will stop our animation.

### All the pieces together 

```

const getNumber = (counter) => {
    return parseFloat(counter.dataset.countTo);
};
const getSpeed = (counter) => {
    return parseFloat(counter.dataset.duration);
};

const updateTex = (counter, text) => {
    counter.textContent = text;
};

const animate = (counter, countTo, duration) => {
    let startTime = null;
    
    let currentTime = Date.now();
    
    const step = (currentTime) => {
        if (!startTime) {
            startTime = currentTime;
        }
        
        const progress = Math.min((currentTime - startTime) / duration, 1);
        
        const currentNum = Math.floor(progress * countTo);
        
        updateTex(counter, currentNum);
        
        if (progress < 1) {
            window.requestAnimationFrame(step);
        } else {
            window.cancelAnimationFrame(window.requestAnimationFrame(step));
        }
    };
    
    window.requestAnimationFrame(step);
};

const counters = document.querySelectorAll('.counter');
counters.forEach((counter) => {
    const countTo = getNumber(counter);
    const animationDuration = getSpeed(counter);
  animate(counter, countTo, animationDuration);
});

``` 
[Demo](https://sarmunbustillo.github.io/animated-counter/)

[Repo](https://github.com/Sarmunbustillo/animated-counter)

[My Twitter] (https://twitter.com/sarmunbustillo?t=pztqXmT7DH2pa9lDLjfrdQ&s=09)