skip to Main Content

Take a look at the following code:

 function getScrollPercent() {
            const h = document.documentElement;
            const st = 'scrollTop';
            const sh = 'scrollHeight';
            const clientHeight = h.clientHeight;
            const scrollTop = h[st];
            const scrollHeight = h[sh];

            return scrollTop / (scrollHeight - clientHeight) * 100;
        }

        function update_scroll() {
            const scrollPercent = getScrollPercent();

            document.getElementById("scroll-line").style.height = `${scrollPercent}%`;
            document.getElementById("progress").textContent = Math.round(scrollPercent) + '%';
        }

        window.addEventListener('scroll', update_scroll);
        update_scroll();
 .content-site {
        height: 500vh;
    }

    #main_box {
        position: fixed;
        right: 50px;
        bottom: 80px;
    }

    #body_box {
        width: 100px;
        height: 100px;
        background: #b0ffcb;
        box-shadow: 0 0 4px #050030;
        border-radius: 50% !important;
        position: relative;
        overflow: hidden;
    }

    #progress {
        position: absolute;
        top: 40%;
        left: auto;
        z-index: 3;
        color: #f00;
        display: block;
        margin: 0 auto;
        width: 100%;
        text-align: center;
    }

    #scroll-line {
        width: 100%;
        position: absolute;
        top: 0;
        left: 0;
        border-radius: 50%;
        z-index: 2;
        background-color: #050030;
    }
    
    
    <div class="content-site"></div>
  
  <footer>
        <div id="main_box">
            <div id="body_box">
                <div id="scroll-line"></div>
                <p id="progress"></p>
            </div>
        </div>
    </footer>

As you can see, I have implemented a progress bar that changes state based on the scroll position of the page.

Now, instead of changing the background color of the element on scroll, I want to create a progress ring around the element that changes its state as the page is scrolled.

Here is an example of what I am looking for:

https://codepen.io/christianmair/pen/dLVbze

The problem with the example above is that it uses SVG, which makes the code very complex.

I want to do this with the code I have written so far, but I have tried many ways and have not been able to achieve the desired result.

2

Answers


  1. Easier solution with SVG.

    function getScrollPercent() {
      const h = document.documentElement;
      const st = 'scrollTop';
      const sh = 'scrollHeight';
      const clientHeight = h.clientHeight;
      const scrollTop = h[st];
      const scrollHeight = h[sh];
      return scrollTop / (scrollHeight - clientHeight) * 100;
    }
    
    function update_scroll() {
      const scrollPercent = getScrollPercent();
      const circle = document.querySelector('.progress-ring__circle');
      const radius = circle.r.baseVal.value;
      const circumference = 2 * Math.PI * radius;
      const offset = circumference - scrollPercent / 100 * circumference;
      circle.style.strokeDashoffset = offset;
      document.getElementById("progress").textContent = Math.round(scrollPercent) + '%';
    }
    window.addEventListener('scroll', update_scroll);
    update_scroll();
    .content-site {
      height: 500vh;
    }
    
    #main_box {
      position: fixed;
      right: 50px;
      bottom: 80px;
    }
    
    #body_box {
      width: 100px;
      height: 100px;
      background: #b0ffcb;
      box-shadow: 0 0 4px #050030;
      border-radius: 50% !important;
      position: relative;
      overflow: hidden;
    }
    
    #progress {
      position: absolute;
      top: 40%;
      left: auto;
      z-index: 3;
      color: #f00;
      display: block;
      margin: 0 auto;
      width: 100%;
      text-align: center;
    }
    
    .progress-ring__circle {
      fill: none;
      stroke: #050030;
      stroke-width: 8;
      stroke-dasharray: 265;
    }
    
    .progress-ring__background {
      fill: none;
      stroke: #b0ffcb;
      stroke-width: 8;
    }
    
    .progress-ring__circle,
    .progress-ring__background {
      stroke-linecap: round;
    }
    <div class="content-site"></div>
    
    <footer>
      <div id="main_box">
        <div id="body_box">
          <svg class="progress-ring" width="100" height="100">
                    <circle class="progress-ring__background" cx="50" cy="50" r="42"></circle>
                    <circle class="progress-ring__circle" cx="50" cy="50" r="42"></circle>
                </svg>
          <div id="progress"></div>
        </div>
      </div>
    </footer>
    Login or Signup to reply.
  2. Try conic-gradient, and add a smaller round which bg color is same as #body_box

    background: conic-gradient(black 0deg, black calc(360deg * var(--scrollPercent)), transparent calc(360deg * var(--scrollPercent)), transparent 360deg);
    
    function getScrollPercent() {
      const { clientHeight, scrollTop, scrollHeight } = document.documentElement;
      return scrollTop / (scrollHeight - clientHeight);
    }
    
    function update_scroll() {
      const scrollPercent = getScrollPercent();
    
      document.getElementById("scroll-line").style.setProperty("--scrollPercent", scrollPercent);
      document.getElementById("progress").textContent = Math.round(scrollPercent * 100) + "%";
    }
    
    window.addEventListener("scroll", update_scroll);
    update_scroll();
    .content-site {
      height: 500vh;
    }
    
    #main_box {
      position: fixed;
      right: 50px;
      bottom: 80px;
    }
    
    #body_box {
      --bg: #b0ffcb;
      width: 100px;
      height: 100px;
      background: var(--bg);
      box-shadow: 0 0 4px #050030;
      border-radius: 50% !important;
      position: relative;
      overflow: hidden;
    }
    
    #progress {
      position: absolute;
      top: 40%;
      left: auto;
      z-index: 3;
      color: #f00;
      display: block;
      margin: 0 auto;
      width: 100%;
      text-align: center;
    }
    
    #scroll-line {
      --scrollPercent: 0;
      width: 100%;
      z-index: 2;
      background: conic-gradient(black 0deg, black calc(360deg * var(--scrollPercent)), transparent calc(360deg * var(--scrollPercent)), transparent 360deg);
    }
    
    #scroll-line::before {
      content: "";
      display: block;
      width: calc(100% - 15px);
      background: var(--bg);
    }
    
    #scroll-line,
    #scroll-line::before {
      aspect-ratio: 1;
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      border-radius: 50%;
    }
    <div class="content-site"></div>
    <footer>
      <div id="main_box">
        <div id="body_box">
          <div id="scroll-line"></div>
          <p id="progress"></p>
        </div>
      </div>
    </footer>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search