0

I have a circular progress bar that animates when the page loads, but I want it to animate when the user scrolls down to it. If the page loads, the user does not see the animation because the animation runs and finishes once they enter or reload the page.

This is my code:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<style>

#skill-body{

padding: 10px;  
min-height: 80vh;
color: #F0505C;
background: #191315;

}

.skill-wrapper{

max-width: 1100px;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
margin: auto;
display: flex;

}

.skill-wrapper .card{

width: calc(100% / 2);
height: 300px;
display: flex;
align-items: center;
justify-content: space-evenly;
flex-direction: column;
margin: auto;

}

.skill-wrapper .card .circle{

position: relative;
height: 150px;
width: 150px;
border-radius: 50%;

}

.skill-wrapper .card .circle .box{

height: 100%;
width: 100%;
transform: translate(-50%, -50%) scale(0.9);
border-radius: 50%;

}

.skill-wrapper .card .circle:hover .box{

transform: translate(-50%, -50%) scale(1.19)

}

.skill-wrapper .card .circle .box,
.skill-wrapper .card .circle .box span{

position: absolute;
top: 50%;
left: 50%;
transition: all 0.1s;

}

.skill-wrapper .card .circle .box span{

font-size: 30px;
transform: translate(-50%, -50%);

}


.skill-wrapper .card .text{

font-size: 20px;

}

</style>

<div class="sec" id="skill-body">
        <div class="skill-wrapper">
                <div class="card">
                    <div class="circle">
                        <div class="bar" data-thickness="8"></div>
                        <div class="box"><span>78%</span></div>
                    </div>
                    <div class="text">Illustrator</div>
                </div>
        
                <div class="card ps">
                    <div class="circle">
                        <div class="bar" data-thickness="8"></div>
                        <div class="box"><span>76%</span></div>
                    </div>
                    <div class="text">Photoshop</div>
                </div>
        </div>
    
        <script>
            let options = {
                startAngle: -1.55,
                size: 150,
                value: 0.78,
                fill: {color: "#F0515C"}
            }
            $(".circle .bar").circleProgress(options).on('circle-animation-progress', function(event, progress, stepValue){
                $(this).parent().find("span").text(String(stepValue.toFixed(2).substr(2)) + "%")
            });

            $(".ps .bar").circleProgress({
                value: 0.76,
            });
        </script>
    </div>
Mun
  • 3
  • 2
  • So you want to check if the circle is visible to the user, based on their scroll position, and then if it is visible, animate it, right? – Krokodil Apr 09 '21 at 14:30
  • I want the "animation of filling up progress bar" to only run when the user enters that specific section. – Mun Apr 09 '21 at 14:37
  • IntersectionObserver may be useful to you here. – A Haworth Apr 09 '21 at 14:44
  • And in the meantime, if the user scrolls up or down such that the progress bar is now not visible, the animation can just to stop where it was. – Mun Apr 09 '21 at 14:45
  • Actually, I did refer to this link here: https://stackoverflow.com/questions/56196030/how-to-animate-circular-progress-bar-on-page-scroll-down But I failed to understand and apply the codes mentioned there. – Mun Apr 09 '21 at 14:47
  • @Mun just give me a few minutes, i've never used this loading effect so ill have to research it a bit – Krokodil Apr 09 '21 at 14:55
  • @AlphaHowl Alright, thank you very much! – Mun Apr 09 '21 at 15:04
  • @Mun No probs xd – Krokodil Apr 09 '21 at 15:16

1 Answers1

0

I can't get my external script reference to work in this snippet, so I'll just use an example I generated (that should, in theory work not only with the spinner, but with any html element).

This example, works with an interval, but you can use onscroll and onresize for better performance. The reason I have used interval is to give the spinner a bit of a delay, so you can actually see it starting to spin.

Anyway, so I've used the getBoundingClientRect method here to return whether the spinner is visible in the current viewport. Then, if it is visible we will add a class to the spinner's classList, otherwise, we will remove that class. In your code, you can instead use the built in methods to start or stop the spinning, if you wish.

With that said, here is the snippet:

const circle = document.getElementById("circle");

function check_if_visible(elmnt) {
    
  var rect = elmnt.getBoundingClientRect();

  return rect.bottom > 0 &&
        rect.right > 0 &&
        rect.left < (window.innerWidth || document.documentElement.clientWidth) /* or $(window).width() */ &&
        rect.top < (window.innerHeight || document.documentElement.clientHeight) /* or $(window).height() */;

}

setInterval(() => {
    if(check_if_visible(circle)) {
    circle.classList.add("turn-around");
  } else {
    circle.classList.remove("turn-around");
  }
}, 1000);
.turn-around * {
  transform-origin: center;
  display: inline-block;
  animation: rot 1050ms infinite linear;
}


@keyframes rot {
  from {transform: rotateZ(0deg)}
  to {transform: rotateZ(360deg)}
}
<div style="font-size: 7vh; color: rgba(0,0,0,0.6); font-family: Comic Sans MS; padding: 30px; padding-bottom: 300px;">
  Lorem ipsum dolor sit amet, consectetur adipisicing elit. Laborum ut, tempore quis consequuntur odio ducimus non, aut incidunt, saepe impedit mollitia? Consectetur adipisicing elit. Commodi sint optio quae culpa sapiente eligendi ad nobis.
<div style="color: red;">To catch the spinner red-handed (ie when it is not spinning), you need to scroll downwards very quickly and keep your eyes peeled!</div>
</div>


<div id="circle" style="height: 6ch; width: 6ch; padding: 10px; font-family: Courier New; position: relative; left: 50%; transform: translateX(-50%); border-radius: 999ch; background: rgba(0,0,0,0.3);">
  <p>Circle</p>
</div>
Krokodil
  • 1,165
  • 5
  • 19