Progress bar made of solid line, with dots as steps

后端 未结 6 1746
[愿得一人]
[愿得一人] 2020-12-01 22:04

I\'m trying to create a vertical progress-bar, with 8 dots on a solid line (with the 8th on the end), where every dot stands for one step in the process. See attached screen

相关标签:
6条回答
  • 2020-12-01 22:06

    My comment turned into an answer. It is using an hidden <progress> with behind an emty <div> to draw the desired output.

    This is not very flexible, CSS must be written in advance for the 5/6 options : 0,20%,40%,60%,80%,100%

    .hide-it {
      position: absolute;
      left: -100%;
    }
    
    #myprogress {
      height: 25em;
      position: relative;
      float: left;
      width: 0.5em;
      background: linear-gradient(to bottom, #00B18E 0%, #CFF8F0 0%, #CFF8F0);
      margin: 1em 9em 1em 1em;
      color:#00B18E;
      box-shadow:0 0 15em 1em black;
    }
    #myprogress:after {
      content: '     Progress 0%';
      position:relative;
      z-index: 1;
      white-space: pre;
      top:-1%;
    }
    #myprogress:before {
      content: '';
      position: absolute;
      z-index: 1;
      top: -0.2em;
      left: -.25em;
      width: 1em;
      height: 1em;
      border-radius: 50%;
      box-shadow: inset 0 0 0 15px #00B18E;
    }
    
    [value="20"] + #myprogress {
      background: linear-gradient(to bottom, #00B18E 20%, #CFF8F0 20%, #CFF8F0);
    }
    
    [value="20"] + #myprogress:before {
      box-shadow: inset 0 0 0 15px #00B18E, 0 5em 0 0 #00B18E;
    }
    [value="20"] + #myprogress:after {
      content:'     Progress 20%';
      top:19%;
    }
    
    [value="40"] + #myprogress {
      background: linear-gradient(to bottom, #00B18E 40%, #CFF8F0 40%, #CFF8F0);
    }
    
    [value="40"] + #myprogress:before {
      box-shadow: inset 0 0 0 15px #00B18E, 0 5em 0 0 #00B18E, 0 10em 0 0 #00B18E;
    }
    [value="40"] + #myprogress:after {
      content:'     Progress 40%';
      top:39%;
    }
    
    [value="60"] + #myprogress {
      background: linear-gradient(to bottom, #00B18E 60%, #CFF8F0 60%, #CFF8F0);
    }
    [value="60"] + #myprogress:before {
      box-shadow: inset 0 0 0 15px #00B18E, 0 5em 0 0 #00B18E, 0 10em 0 0 #00B18E, 0 15em 0 0 #00B18E;
    }
    [value="60"] + #myprogress:after {
      content:'     Progress 60%';
      top:59%;
    }
    [value="80"] + #myprogress {
      background: linear-gradient(to bottom, #00B18E 80%, #CFF8F0 80%, #CFF8F0);
    }
    [value="80"] + #myprogress:before {
      box-shadow: inset 0 0 0 15px #00B18E, 0 5em 0 0 #00B18E, 0 10em 0 0 #00B18E, 0 15em 0 0 #00B18E, 0 20em 0 0 #00B18E;
    }
    [value="80"] + #myprogress:after {
      content:'     Progress 80%';
      top:79%;
    }
    [value="100"] + #myprogress {
      background: linear-gradient(to bottom, #00B18E 100%, #CFF8F0 100%, #CFF8F0);
    }
    [value="100"] + #myprogress:before {
      box-shadow: inset 0 0 0 15px #00B18E, 0 5em 0 0 #00B18E, 0 10em 0 0 #00B18E, 0 15em 0 0 #00B18E, 0 20em 0 0 #00B18E, 0 25em 0 0 #00B18E;
    }
    [value="100"] + #myprogress:after {
      content:'     Progress 100%';
      top:99%;
    }
    <progress class="hide-it" value="0" max="100">
    </progress>
      <div id="myprogress"></div>
    <progress class="hide-it" value="20" max="100">
    </progress>
      <div id="myprogress"></div>
    <progress class="hide-it" value="40" max="100">
    </progress>
      <div id="myprogress"></div>
    <progress class="hide-it" value="60" max="100">
    </progress>
      <div id="myprogress"></div>
    <progress class="hide-it" value="80" max="100">
    </progress>
      <div id="myprogress"></div>
    <progress class="hide-it" value="100" max="100">
    </progress>
      <div id="myprogress"></div>

    The codepen to play with

    0 讨论(0)
  • 2020-12-01 22:08

    Here is a CSS solution with very minimal no. of elements. In this approach we use a combination of linear-gradients and radial-gradients to produce the vertical line and the dots.

    The parent #progress-bar element produces the lightgreen (initial) line and circles while the same gradients are added to the child #progress-now element which is positioned absolutely with respect to the parent. The only difference is that the height of the #progress-now element is determined based on the value attribute.

    The solution would work even when the values are in fractions. I know you are using it for step tracker but this is just an added use (blowing my own trumpet :D).

    window.onload = function() {
      var val = document.getElementById('progress-now').getAttribute('value');
      var progress = (val * 50 > 400) ? 400 : val*50; /* 50 is 1/8th of height, 400 is height */
      document.getElementById('progress-now').setAttribute('style', 'height: ' + progress + 'px');
    }
    #progress-bar {
      position: relative;
      height: 400px;
      width: 200px;
      background: linear-gradient(to bottom, lightgreen 99.9%, transparent 99.9%), radial-gradient(circle at 50% 50%, lightgreen 25%, transparent 30%);
      background-position: 50% 0%, 50% 15px; /* 15px is 30% of 50px */
      background-size: 5px 100%, 50px 50px; /* 5px is the thickness of the bar, 50px is 1/8th of the height */
      background-repeat: no-repeat, repeat-y;
    }
    #progress-now {
      position: absolute;
      width: 200px;
      background: linear-gradient(to bottom, darkgreen 99.9%, transparent 99.9%), radial-gradient(circle at 50% 50%, darkgreen 25%, transparent 30%);
      background-position: 50% 0%, 50% 15px;
      background-size: 5px 100%, 50px 50px;
      background-repeat: no-repeat, repeat-y;
      z-index: 1;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
    <div id='progress-bar'>
      <div id='progress-now' value='1.85'></div>
    </div>


    Below is a version with fill-up animation effect.

    window.onload = function() {
      var val = 0, progress = 0;
    
      function progressBar() {
        val += 0.005;
        progress = (val * 50 > 400) ? 400 : val * 50; /* 50 is 1/8th of height, 400 is height */
        document.getElementById('progress-now').setAttribute('style', 'height: ' + progress + 'px');
        if (val <= 8) anim = window.requestAnimationFrame(progressBar);
      }
      
      progressBar();
    }
    #progress-bar {
      position: relative;
      height: 400px;
      width: 200px;
      background: linear-gradient(to bottom, lightgreen 99.9%, transparent 99.9%), radial-gradient(circle at 50% 50%, lightgreen 25%, transparent 30%);
      background-position: 50% 0%, 50% 15px; /* 15px is 30% of 50px */
      background-size: 5px 100%, 50px 50px; /* 5px is the thickness of the bar, 50px is 1/8th of the height */
      background-repeat: no-repeat, repeat-y;
    }
    #progress-now {
      position: absolute;
      width: 200px;
      background: linear-gradient(to bottom, darkgreen 99.9%, transparent 99.9%), radial-gradient(circle at 50% 50%, darkgreen 25%, transparent 30%);
      background-position: 50% 0%, 50% 15px;
      background-size: 5px 100%, 50px 50px;
      background-repeat: no-repeat, repeat-y;
      z-index: 1;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
    <div id='progress-bar'>
      <div id='progress-now'></div>
    </div>

    0 讨论(0)
  • 2020-12-01 22:17

    My solution is similar to @Stewartside's, except it uses Flexbox to position everything equally. It's also really easy to change the height.

    ul.progress-bar {
      height: 300px;
      list-style: none;
      margin: 0;
      padding: 0;
      position: relative;
      display: flex;
      flex-direction: column;
      justify-content: space-between;
      overflow: hidden;
    }
    ul.progress-bar::after {
      content: "";
      position: absolute;
      top: 0;
      left: 5px;
      background: #777;
      width: 5px;
      height: 100vh;
    }
    ul.progress-bar li {
      background: #000;
      border-radius: 100px;
      width: 15px;
      height: 15px;
      z-index: 1;
      position: relative;
    }
    ul.progress-bar li.stop ~ li {
      background: #777;
    }
    ul.progress-bar li.stop ~ li::after {
      height: 0;
    }
    ul.progress-bar li::after {
      content: "";
      position: absolute;
      bottom: 0;
      left: 5px;
      background: #000;
      width: 5px;
      height: 100vh;
    }
    <ul class="progress-bar">
        <li>
        <li>
        <li>
        <li class="stop">
        <li>
    </ul>

    For some reason, the bottom segment doesn't seem to show up in the stacksnippet, so here it is on jsfiddle.

    0 讨论(0)
  • 2020-12-01 22:23

    CSS

    My CSS solution is based around multiple div's which allow you to have as many as you like and determine how many are completed.

    .complete {
      width: 5px;
      height: 50px;
      position: relative;
      background: green;
      margin-left: 8px;
    }
    .complete:after {
      content: '';
      width: 20px;
      height: 20px;
      border-radius: 50%;
      position: absolute;
      bottom: -7.5px;
      left: -8px;
      background: green;
      z-index: 100;
    }
    .not_complete {
      width: 5px;
      height: 50px;
      background: lightgreen;
      position: relative;
      margin-left: 8px;
    }
    .not_complete:after {
      content: '';
      width: 20px;
      height: 20px;
      border-radius: 50%;
      position: absolute;
      bottom: -7.5px;
      left: -8px;
      background: lightgreen;
      z-index: 100;
    }
    <div class="progress">
      <div class="complete"></div>
      <div class="complete"></div>
      <div class="not_complete"></div>
      <div class="not_complete"></div>
      <div class="not_complete"></div>
      <div class="not_complete"></div>
      <div class="not_complete"></div>
      <div class="not_complete"></div>
    </div>

    SVG

    This is also possible using a SVG g element and using it with different fills.

    Just remember that the height of the SVG needs to be the same or greater than all the elements put together.

    <svg width="20" height="445" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
      <defs>
        <g id="completed">
          <line x1="0" y1="5" x2="0" y2="40" style="stroke:green;stroke-width:3;" />
          <circle style="fill:green; stroke-width:0;" cx="0" cy="50" r="10" />
        </g>
        <g id="not_complete">
          <line x1="0" y1="5" x2="0" y2="40" style="stroke:lightgreen;stroke-width:3;" />
          <circle style="fill:lightgreen; stroke-width:0;" cx="0" cy="50" r="10" />
        </g>
      </defs>
    
      <use x="10" y="00" xlink:href="#completed" />
      <use x="10" y="55" xlink:href="#completed" />
      <use x="10" y="110" xlink:href="#not_complete" />
      <use x="10" y="165" xlink:href="#not_complete" />
      <use x="10" y="220" xlink:href="#not_complete" />
      <use x="10" y="275" xlink:href="#not_complete" />
      <use x="10" y="330" xlink:href="#not_complete" />
      <use x="10" y="385" xlink:href="#not_complete" />
    </svg>

    0 讨论(0)
  • 2020-12-01 22:23

    You can use box-shadow to achieve this effect.This can be achieved using a single element as it uses box-shadows but it will be hard to change the color using javascript

    Note:I used jquery to only show the effect of adding more to the progress bar click anywhere on the body to see the result

    $('body').click(function () {
        var x = parseInt($('.progressNow').css('top')) + 10;
        $('.progressNow').css({
            top: x
        })
        if (x > 90 - 800) {      $('.circle').css('background','#00b164')
        }
        if(x > 180 -800){
            $('.circle').css('box-shadow','0 90px 0 0 #00b164, 0 180px 0 0 #8fe4bf, 0 270px 0 0 #8fe4bf, 0 360px 0 0 #8fe4bf, 0 450px 0 0 #8fe4bf, 0 540px 0 0 #8fe4bf')
        }
        if(x > 270 -800){
            $('.circle').css('box-shadow','0 90px 0 0 #00b164, 0 180px 0 0 #00b164, 0 270px 0 0 #8fe4bf, 0 360px 0 0 #8fe4bf, 0 450px 0 0 #8fe4bf, 0 540px 0 0 #8fe4bf')
        }    
    })
    #progress {
        overflow:hidden;
        width:15px;
        padding-left:5px;
        height:800px;
        position: relative;
    }
    #progress .progressBar {
        height: 800px;
        width: 6px;
        background: #8fe4bf;
        position: absolute;
    }
    #progress .progressNow {
        height: 800px;
        top:-800px;
        width: 6px;
        background: #00b164;
        position: absolute;
    }
    .circle{
        content:"";
        width: 16px;
        height: 16px;
        border-radius: 50%;
        background: #8fe4bf;
        display: block;
        margin-left: -5px;
        position: absolute;
        top: 90px;
        box-shadow:0 90px 0 0 #8fe4bf, 0 180px 0 0 #8fe4bf, 0 270px 0 0 #8fe4bf, 0 360px 0 0 #8fe4bf, 0 450px 0 0 #8fe4bf, 0 540px 0 0 #8fe4bf;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
    <div id="progress">
        <div class="progressBar"></div>
        <div class="circle"></div>
        <div class="progressNow" value="1"></div>
    </div>

    0 讨论(0)
  • 2020-12-01 22:29

    SVG

    Here is a functional scroll bar with series of dots.
    The dots are a single path. The scrolling effect on the bar is a linear gradient modified through javascript.
    The percentage text is a svg text element, and javascript modifies the y attribute of this element.

    (I spent to much time on this solution)

    var start = document.getElementById("gradstart");
    var stop = document.getElementById("gradstop");
    var max = Math.max(document.body.scrollHeight, document.body.offsetHeight,
      document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight);
    max -= document.documentElement.clientHeight;
    
    var process = document.getElementById("process");
    
    function gradientScroll() {
      var top = (window.pageYOffset || document.documentElement.scrollTop)
      var amount = Math.round((top / max) * 100.0);
      process.setAttribute("y", (amount) + "%");
      process.innerHTML = amount + "%";
      start.setAttribute("offset", amount + "%");
      stop.setAttribute("offset", amount + "%");
    }
    
    window.addEventListener("scroll", function() {
      window.requestAnimationFrame(gradientScroll);
    });
    <svg height="800px" viewBox="0 0 100 700" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink">
      <defs>
        <linearGradient x1="0" x2="0" y1="0%" y2="100%" id="grad">
          <stop id="gradstart" offset="50%" stop-color="firebrick" />
          <stop id="gradstop" offset="50%" stop-color="pink" />
        </linearGradient>
      </defs>
      <path fill="url(#grad)" d="m20,0 0,50 
                           a10 10 0 0 0 0,20
                           v50
                           a10 10 0 0 0 0,20
                           v50
                           a10 10 0 0 0 0,20
                           v50
                           a10 10 0 0 0 0,20
                           v50
                           a10 10 0 0 0 0,20
                           v50
                           a10 10 0 0 0 0,20
                           v50
                           a10 10 0 0 0 0,20
                           v50
                           a10 10 0 0 0 0,20
                           v50
                           a10 10 0 0 0 0,20
                           v50
                           a10 10 0 0 0 0,20
                           h5
                           a10 10 0 0 0 0,-20
                           v-50
                           a10 10 0 0 0 0,-20
                           v-50
                           a10 10 0 0 0 0,-20
                           v-50
                           a10 10 0 0 0 0,-20
                           v-50
                           a10 10 0 0 0 0,-20
                           v-50
                           a10 10 0 0 0 0,-20
                           v-50
                           a10 10 0 0 0 0,-20
                           v-50
                           a10 10 0 0 0 0,-20
                           v-50
                           a10 10 0 0 0 0,-20
                           v-50
                           a10 10 0 0 0 0,-20
                           v-100Z"></path>
      <text id="process" text-anchor="middle" x="60" y="50">Amount</text>
    </svg>

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