Count down timer with circular progress bar

前端 未结 1 1327
有刺的猬
有刺的猬 2020-11-28 16:57

I created an countdown timer. I got a border which is made circular. As the timer is tending towards zero, the circular border should change color with decrement in the seco

相关标签:
1条回答
  • 2020-11-28 17:23

    Below is a sample snippet for the countdown timer with a circular progress bar that changes color as the value comes down.

    Basically what we are doing is the follows: (refer inline comments in code for more details)

    • 4 additional div are absolutely positioned on top of the parent. Each represents a quadrant.
    • Initially the skew angle on all of them is 0 degree, so they are all fully visible and cover the entire parent. This hides the box-shadow of the parent and thus makes it look like a solid circle.
    • At every iteration, we modify the skew angle of each quadrant (div) such that the quadrants eventually become invisible one by one and thus revealing the box-shadow of parent.
    • The quadrants become invisible when the skew angle reaches +/- 90 degrees and so at each iteration the angle is calculated as (90deg / no. of steps covered in that quadrant).
    • As the progress moves past one quadrant to another, the box-shadow of the parent is changed to give the appearance of the progress bar changing its color.
    • The original CodePen uses the data-progress attribute's value directly as the content of a pseudo-element. But that value gets incremented with every iteration. Since it is also used in calculation of the skew angles, I have left it as-is and used a separate field for the countdown timer. Content of pseudo-elements cannot be set using JS and so I have added another div for the timer text.

    window.onload = function() {
      var progressbar = document.querySelector('div[data-progress]'),
        quad1 = document.querySelector('.quad1'),
        quad2 = document.querySelector('.quad2'),
        quad3 = document.querySelector('.quad3'),
        quad4 = document.querySelector('.quad4'),
        counter = document.querySelector('.counter');
    
      var progInc = setInterval(incrementProg, 1000); // call function every second
    
      function incrementProg() {
        progress = progressbar.getAttribute('data-progress'); //get current value
        progress++; // increment the progress bar value by 1 with every iteration
        progressbar.setAttribute('data-progress', progress); //set value to attribute
        counter.textContent = 100 - parseInt(progress, 10); // set countdown timer's value
        setPie(progress); // call the paint progress bar function based on progress value
        if (progress == 100) {
          clearInterval(progInc); // clear timer when countdown is complete
        }
      }
    
      function setPie(progress) {
        /* If progress is less than 25, modify skew angle the first quadrant */
        if (progress <= 25) {
          quad1.setAttribute('style', 'transform: skew(' + progress * (-90 / 25) + 'deg)');
        }
    
        /* Between 25-50, hide 1st quadrant + modify skew angle of 2nd quadrant */
        else if (progress > 25 && progress <= 50) {
          quad1.setAttribute('style', 'transform: skew(-90deg)'); // hides 1st completely
          quad2.setAttribute('style', 'transform: skewY(' + (progress - 25) * (90 / 25) + 'deg)');
          progressbar.setAttribute('style', 'box-shadow: inset 0px 0px 0px 20px orange'); 
        }
    
        /* Between 50-75, hide first 2 quadrants + modify skew angle of 3rd quadrant */
        else if (progress > 50 && progress <= 75) {
          quad1.setAttribute('style', 'transform: skew(-90deg)'); // hides 1st completely
          quad2.setAttribute('style', 'transform: skewY(90deg)'); // hides 2nd completely
          quad3.setAttribute('style', 'transform: skew(' + (progress - 50) * (-90 / 25) + 'deg)');
          progressbar.setAttribute('style', 'box-shadow: inset 0px 0px 0px 20px yellow');
        }
    
        /* Similar to above for value between 75-100 */
        else if (progress > 75 && progress <= 100) {
          quad1.setAttribute('style', 'transform: skew(-90deg)'); // hides 1st completely
          quad2.setAttribute('style', 'transform: skewY(90deg)'); // hides 2nd completely
          quad3.setAttribute('style', 'transform: skew(-90deg)'); // hides 3rd completely
          quad4.setAttribute('style', 'transform: skewY(' + (progress - 75) * (90 / 25) + 'deg)');
          progressbar.setAttribute('style', 'box-shadow: inset 0px 0px 0px 20px green');
        }
      }
    }
    div[data-progress] {
      box-sizing: border-box;
      position: relative;
      height: 200px;
      width: 200px;
      background: beige;
      border-radius: 50%;
      box-shadow: inset 0px 0px 0px 20px red;
      transition: all 1s;
      overflow: hidden;
    }
    .counter {
      position: absolute;
      height: 100%;
      width: 100%;
      top: 0%;
      left: 0%;
      text-align: center;
      line-height: 200px;
      border-radius: 50%;
      background: transparent;
      z-index: 4;
    }
    div > div {
      position: absolute;
      height: 50%;
      width: 50%;
      background: inherit;
      border-radius: 0%;
    }
    .quad1,
    .quad2 {
      left: 50%;
      transform-origin: left bottom;
    }
    .quad3,
    .quad4 {
      left: 0%;
      transform-origin: right top;
    }
    .quad1,
    .quad4 {
      top: 0%;
    }
    .quad2,
    .quad3 {
      top: 50%;
    }
    .quad1,
    .quad3 {
      transform: skew(0deg); /* invisible at -90deg */
    }
    .quad2,
    .quad4 {
      transform: skewY(0deg); /* invisible at 90deg */
    }
    
    /* Just for demo */
    
    body {
      background: linear-gradient(90deg, crimson, indianred, purple);
    }
    div[data-progress] {
      margin: 40px auto;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
    <div data-progress="0">
      <div class="quad1"></div>
      <div class="quad2"></div>
      <div class="quad3"></div>
      <div class="quad4"></div>
      <div class='counter'>100</div>
    </div>


    In the below snippet, I have added a different background for each quadrant to provide a better visual illustration of what exactly is happening.

    window.onload = function() {
      var progressbar = document.querySelector('div[data-progress]'),
        quad1 = document.querySelector('.quad1'),
        quad2 = document.querySelector('.quad2'),
        quad3 = document.querySelector('.quad3'),
        quad4 = document.querySelector('.quad4'),
        counter = document.querySelector('.counter');
    
      var progInc = setInterval(incrementProg, 1000); // call function every second
    
      function incrementProg() {
        progress = progressbar.getAttribute('data-progress'); //get current value
        progress++; // increment the progress bar value by 1 with every iteration
        progressbar.setAttribute('data-progress', progress); //set value to attribute
        counter.textContent = 100 - parseInt(progress, 10); // set countdown timer's value
        setPie(progress); // call the paint progress bar function based on progress value
        if (progress == 100) {
          clearInterval(progInc); // clear timer when countdown is complete
        }
      }
    
      function setPie(progress) {
        /* If progress is less than 25, modify skew angle the first quadrant */
        if (progress <= 25) {
          quad1.setAttribute('style', 'transform: skew(' + progress * (-90 / 25) + 'deg)');
        }
    
        /* Between 25-50, hide 1st quadrant + modify skew angle of 2nd quadrant */
        else if (progress > 25 && progress <= 50) {
          quad1.setAttribute('style', 'transform: skew(-90deg)'); // hides 1st completely
          quad2.setAttribute('style', 'transform: skewY(' + (progress - 25) * (90 / 25) + 'deg)');
          progressbar.setAttribute('style', 'box-shadow: inset 0px 0px 0px 20px orange');
        }
    
        /* Between 50-75, hide first 2 quadrants + modify skew angle of 3rd quadrant */
        else if (progress > 50 && progress <= 75) {
          quad1.setAttribute('style', 'transform: skew(-90deg)'); // hides 1st completely
          quad2.setAttribute('style', 'transform: skewY(90deg)'); // hides 2nd completely
          quad3.setAttribute('style', 'transform: skew(' + (progress - 50) * (-90 / 25) + 'deg)');
          progressbar.setAttribute('style', 'box-shadow: inset 0px 0px 0px 20px yellow');
        }
    
        /* Similar to above for value between 75-100 */
        else if (progress > 75 && progress <= 100) {
          quad1.setAttribute('style', 'transform: skew(-90deg)'); // hides 1st completely
          quad2.setAttribute('style', 'transform: skewY(90deg)'); // hides 2nd completely
          quad3.setAttribute('style', 'transform: skew(-90deg)'); // hides 3rd completely
          quad4.setAttribute('style', 'transform: skewY(' + (progress - 75) * (90 / 25) + 'deg)');
          progressbar.setAttribute('style', 'box-shadow: inset 0px 0px 0px 20px green');
        }
      }
    }
    div[data-progress] {
      box-sizing: border-box;
      position: relative;
      height: 200px;
      width: 200px;
      background: beige;
      border-radius: 50%;
      box-shadow: inset 0px 0px 0px 20px red;
      transition: all 1s;
      overflow: hidden;
    }
    .counter {
      position: absolute;
      height: 100%;
      width: 100%;
      top: 0%;
      left: 0%;
      text-align: center;
      line-height: 200px;
      border-radius: 50%;
      background: transparent;
      z-index: 4;
    }
    div > div {
      position: absolute;
      height: 50%;
      width: 50%;
      border-radius: 0%;
    }
    .quad1,
    .quad2 {
      left: 50%;
      transform-origin: left bottom;
    }
    .quad3,
    .quad4 {
      left: 0%;
      transform-origin: right top;
    }
    .quad1, .quad4{
      top: 0%;
    }
    .quad2, .quad3{
      top: 50%;
    }
    .quad1, .quad3{
      transform: skew(0deg);
    }
    .quad2, .quad4{
      transform: skewY(0deg);
    }
    
    /* Just for demo */
    
    body {
      background: linear-gradient(90deg, crimson, indianred, purple);
    }
    div[data-progress] {
      margin: 40px auto;
    }
    .quad1 {
      background: blue;
    }
    .quad2 {
      background: pink;
    }
    .quad3 {
      background: tan;
    }
    .quad4 {
      background: teal;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
    <div data-progress="0">
      <div class="quad1"></div>
      <div class="quad2"></div>
      <div class="quad3"></div>
      <div class="quad4"></div>
      <div class='counter'>100</div>
    </div>

    0 讨论(0)
提交回复
热议问题