Home > Mobile >  How to add a smooth animation to the progress bar
How to add a smooth animation to the progress bar

Time:10-27

When I click I want to smoothly add segments to the progress bar. They are added but instantly. What could be the problem?

demonstration of problem

I tried to implement a smooth animation with setInterval, but nothing comes out. Percentages are also added instantly.

let progressBar = document.querySelector(".progressbar");
let progressBarValue = document.querySelector(".progressbar__value");
const body = document.querySelector("body");

let progressBarStartValue = 0;
let progressBarEndValue = 100;
let speed = 50;

body.addEventListener("click", function(e) {
  if (progressBarStartValue === progressBarEndValue) {
    alert("you have completed all the tasks");
  } else {
    let progress = setInterval(() => {
      if (progressBarStartValue != 100) {
        progressBarStartValue  = 10;
        clearInterval(progress);
      }
      progressBarValue.textContent = `${progressBarStartValue}%`;
      progressBar.style.background = `conic-gradient(
                #FFF ${progressBarStartValue * 3.6}deg,
                #262623 ${progressBarStartValue * 3.6}deg
            )`;
    }, speed);
  }
});
.progressbar {
  position: relative;
  height: 150px;
  width: 150px;
  background-color: #262623;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.progressbar::before {
  content: "";
  position: absolute;
  height: 80%;
  width: 80%;
  background-color: #0f0f0f;
  border-radius: 50%;
}

.progressbar__value {
  color: #fff;
  z-index: 9;
  font-size: 25px;
  font-weight: 600;
}
<main >
  <section >
    <div >
      <div >
        <div >
          <h2 >You're almost there!</h2>
          <p >keep up the good work</p>
        </div>
        <div ><span >0%</span></div>
      </div>
    </div>
  </section>
</main>

CodePudding user response:

This may not be exactly what you're looking for, but with the conic-gradient() implementation you're using, I'd recommend checking out a library call anime.js.

Here's an example with your implementation (same html and css):

// your.js

let progressBar = document.querySelector(".progressbar");
let progressBarValue = document.querySelector(".progressbar__value");
const body = document.querySelector("body");

// Switched to object for target in anime()
let progressBarObject = {
  progressBarStartValue: 0,
  progressBarEndValue: 100,
  progressBarAnimationValue: 0 * 3.6 // New value needed for smoothing the progress bar, since the progress value needs to be multiplied by 3.6
}

// Not necessary, but I recommend changing the event listener to pointerup for better support
// Also not necessary, I changed function to arrow function for my own preference
body.addEventListener("pointerup", e => {
  e.preventDefault()
  if (progressBarObject.progressBarStartValue === progressBarObject.progressBarEndValue) {
    alert("you have completed all the tasks");
  } else {
    let newValue = 0 // Needed so we can set the value, before it's applied in anime()
    if (progressBarObject.progressBarStartValue != 100) {
      // Math.ceil() allows us to round to the nearest 10 to guarantee the correct output
      newValue = Math.ceil((progressBarObject.progressBarStartValue   10) / 10) * 10;
    }
    // Optional: Prevents accidentally going over 100 somehow
    if (newValue > 100) {
      newValue = 100
    }
    anime({
      targets: progressBarObject,
      progressBarStartValue: newValue,
      progressBarAnimationValue: newValue * 3.6,
      easing: 'easeInOutExpo',
      round: 1, // Rounds to nearest 1 so you don't have 0.3339...% displayed in progressBarValue
      update: () => {
        progressBar.style.backgroundImage = `conic-gradient(
          #FFF ${progressBarObject.progressBarAnimationValue}deg,
          #262623 ${progressBarObject.progressBarAnimationValue}deg)`;
        progressBarValue.textContent = `${progressBarObject.progressBarStartValue}%`;
      },
      duration: 500
    });
  }
});

Here's a CodePen using the anime.js CDN: Circular Progress Bar Smoothing

If you don't want to use a javascript library, then I'd recommend switching from the conic-gradient() to something else. I hear using an .svg circle with stroke and stroke-dasharray can work great with CSS transition.

CodePudding user response:

You shouldn't setInterval your progress variable like this. instead, put it as a global variable outside the function then use it to gradually add 1 as long as the start value is less than progress, and you still can control the speed with your speed variable.

let progressBar = document.querySelector(".progressbar");
  let progressBarValue = document.querySelector(".progressbar__value");
  const body = document.querySelector("body");
  
  let progressBarStartValue = 0;
  let progressBarEndValue = 100;
  let speed = 50;
  let progress = 0;
  
  body.addEventListener("click", function(e) {
    if (progressBarStartValue === progressBarEndValue) {
      alert("you have completed all the tasks");
    } else {
      
      progress  = 10;
      
       setInterval(() => {
        if (progressBarStartValue < progress) {
          progressBarStartValue  = 1;
          clearInterval();
        }

        progressBarValue.textContent = `${progressBarStartValue}%`;
        progressBar.style.background = `conic-gradient(
                  #FFF ${progressBarStartValue * 3.6}deg,
                  #262623 ${progressBarStartValue * 3.6}deg
              )`;
      }, speed);
    }
  });
.progressbar {
  position: relative;
  height: 150px;
  width: 150px;
  background-color: #262623;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 3px solid red;
  
}

.progressbar::before {
  content: "";
  position: absolute;
  height: 80%;
  width: 80%;
  background-color: #0f0f0f;
  border-radius: 50%;
  border: 3px solid blue;
}

.progressbar__value {
  color: #fff;
  z-index: 9;
  font-size: 25px;
  font-weight: 600;
}
<main >
    <section >
      <div >
        <div >
          <div >
            <h2 >You're almost there!</h2>
            <p >keep up the good work</p>
          </div>
          <div ><span >0%</span></div>
        </div>
      </div>
    </section>
  </main>

  • Related