Search code examples
csssvgcss-animationsstroke-dasharray

SVG Counterclockwise


For the project I'm working on, I have to be able to depict values from -100% to 100%. Is there any way that I can take an existing percentage circle such as the one below and make the progress indicator go backwards - as in counterclockwise?

<div class="flex-wrapper">

  <div class="single-chart">
    <svg viewbox="0 0 36 36" class="circular-chart orange">
      <path class="circle-bg"
        d="M18 2.0845
          a 15.9155 15.9155 0 0 1 0 31.831
          a 15.9155 15.9155 0 0 1 0 -31.831"
      />
      <path class="circle"
        stroke-dasharray="50, 100"
        d="M18 2.0845
          a 15.9155 15.9155 0 0 1 0 31.831
          a 15.9155 15.9155 0 0 1 0 -31.831"
      />
      <text x="18" y="20.35" class="percentage">30%</text>
    </svg>
  </div>

</div> 

CSS:

.flex-wrapper {
  display: flex;
  flex-flow: row nowrap;
}

.single-chart {
  width: 5%;
  justify-content: space-around ;
}

.circular-chart {
  display: block;
  margin: 10px auto;
  max-width: 80%;
  max-height: 250px;
}

.circle-bg {
  fill: none;
  stroke: #eee;
  stroke-width: 2.0;
}

.circle {
  fill: none;
  stroke-width: 2.0;
  stroke-linecap: square;
  animation: progress 1s ease-out forwards;
}

@keyframes progress {
  0% {
    stroke-dasharray: 0 100;
  }
}

@keyframes antiprogress {
  0% {
    stroke-dasharray: 0 100;
  }
}


.circular-chart.orange .circle {
  stroke: #f80;
}

.circular-chart.green .circle {
  stroke: #00c851;
}

.circular-chart.blue .circle {
  stroke: #33b5e5;
}

.circular-chart.blue .red {
  stroke: #ff3547;
}

.percentage {
  fill: #666;
  font-family: sans-serif;
  font-size: 0.5em;
  text-anchor: middle;
}

Here's a link to the codepen: https://codepen.io/killia15/pen/zRqvxP

Image of Desired Output


Solution

  • UPDATE : the first answer seems to work only on chrome due to some browser compatibility, you can find below a more suitable answer

    You may combine the use of rotation. So if you want -20% you make the stroke-dasharray="20, 100" and you apply a negative rotation calculated in this way = 20% of 360 degree which will be in this case -72. Then add to the animation rotate(0) to create the anti-clockwise movement:

    .flex-wrapper {
      display: flex;
      flex-flow: row nowrap;
    }
    
    .single-chart {
      width: 33%;
      justify-content: space-around;
    }
    
    .circular-chart {
      display: block;
      margin: 10px auto;
      max-width: 80%;
      max-height: 250px;
    }
    
    .circle-bg {
      fill: none;
      stroke: #eee;
      stroke-width: 3.8;
    }
    
    .circle {
      fill: none;
      stroke-width: 3.8;
      stroke-linecap: square;
      transform-origin:center;
      animation: progress 1s ease-out forwards;
    }
    .circle-anti {
      animation: antiprogress 1s ease-out forwards;
    }
    
    @keyframes progress {
      0% {
        stroke-dasharray: 0 100;
      }
    }
    
    @keyframes antiprogress {
      0% {
        stroke-dasharray: 0 100;
        transform: rotate(0deg);
      }
    }
    
    .circular-chart.orange .circle {
      stroke: #f80;
    }
    
    .circular-chart.green .circle {
      stroke: #00c851;
    }
    
    .circular-chart.blue .circle {
      stroke: #33b5e5;
    }
    
    .circular-chart.blue .red {
      stroke: #ff3547;
    }
    
    .percentage {
      fill: #666;
      font-family: sans-serif;
      font-size: 0.5em;
      text-anchor: middle;
    }
    <div class="flex-wrapper">
    
      <div class="single-chart">
        <svg viewbox="0 0 36 36" class="circular-chart orange">
          <path class="circle-bg"
            d="M18 2.0845
              a 15.9155 15.9155 0 0 1 0 31.831
              a 15.9155 15.9155 0 0 1 0 -31.831"
          />
          <path class="circle circle-anti"
            stroke-dasharray="50, 100" style="transform:rotate(-180deg)"
            d="M18 2.0845
              a 15.9155 15.9155 0 0 1 0 31.831
              a 15.9155 15.9155 0 0 1 0 -31.831"
          />
          <text x="18" y="20.35" class="percentage">-50%</text>
        </svg>
      </div>
    
      <div class="single-chart">
        <svg viewbox="0 0 36 36" class="circular-chart green">
          <path class="circle-bg"
            d="M18 2.0845
              a 15.9155 15.9155 0 0 1 0 31.831
              a 15.9155 15.9155 0 0 1 0 -31.831"
          />
          <path class="circle"
            stroke-dasharray="60, 100"
            d="M18 2.0845
              a 15.9155 15.9155 0 0 1 0 31.831
              a 15.9155 15.9155 0 0 1 0 -31.831"
          />
          <text x="18" y="20.35" class="percentage">60%</text>
        </svg>
      </div>
      
       <div class="single-chart">
        <svg viewbox="0 0 36 36" class="circular-chart green">
          <path class="circle-bg"
            d="M18 2.0845
              a 15.9155 15.9155 0 0 1 0 31.831
              a 15.9155 15.9155 0 0 1 0 -31.831"
          />
          <path style="transform:rotate(-72deg)" class="circle circle-anti"
            stroke-dasharray="20, 100"
            d="M18 2.0845
              a 15.9155 15.9155 0 0 1 0 31.831
              a 15.9155 15.9155 0 0 1 0 -31.831"
          />
          <text x="18" y="20.35" class="percentage">-20%</text>
        </svg>
      </div>
    
    </div>

    UPDATE

    Here is another easier idea without the need of animating the rotation. I simply use a scale(-1) in order to inverse the shape and thus it will go to the left:

    .flex-wrapper {
      display: flex;
      flex-flow: row nowrap;
    }
    
    .single-chart {
      width: 33%;
      justify-content: space-around;
    }
    
    .circular-chart {
      display: block;
      margin: 10px auto;
      max-width: 80%;
      max-height: 250px;
    }
    
    .circle-bg {
      fill: none;
      stroke: #eee;
      stroke-width: 3.8;
    }
    
    .circle {
      fill: none;
      stroke-width: 3.8;
      stroke-linecap: square;
      transform-origin:center;
      animation: progress 1s ease-out forwards;
    }
    .circle-anti {
      animation: antiprogress 1s ease-out forwards;
    }
    
    @keyframes progress {
      0% {
        stroke-dasharray: 0 100;
      }
    }
    
    @keyframes antiprogress {
      0% {
        stroke-dasharray: 0 100;
        transform: rotate(0deg);
      }
    }
    
    .circular-chart.orange .circle {
      stroke: #f80;
    }
    
    .circular-chart.green .circle {
      stroke: #00c851;
    }
    
    .circular-chart.blue .circle {
      stroke: #33b5e5;
    }
    
    .circular-chart.blue .red {
      stroke: #ff3547;
    }
    
    .percentage {
      fill: #666;
      font-family: sans-serif;
      font-size: 0.5em;
      text-anchor: middle;
    }
    <div class="flex-wrapper">
    
      <div class="single-chart">
        <svg viewbox="0 0 36 36" class="circular-chart orange">
          <path class="circle-bg"
            d="M18 2.0845
              a 15.9155 15.9155 0 0 1 0 31.831
              a 15.9155 15.9155 0 0 1 0 -31.831"
          />
          <path class="circle"
            stroke-dasharray="50, 100" transform="scale(-1,1)"
            d="M18 2.0845
              a 15.9155 15.9155 0 0 1 0 31.831
              a 15.9155 15.9155 0 0 1 0 -31.831"
          />
          <text x="18" y="20.35" class="percentage">-50%</text>
        </svg>
      </div>
    
      <div class="single-chart">
        <svg viewbox="0 0 36 36" class="circular-chart green">
          <path class="circle-bg"
            d="M18 2.0845
              a 15.9155 15.9155 0 0 1 0 31.831
              a 15.9155 15.9155 0 0 1 0 -31.831"
          />
          <path class="circle"
            stroke-dasharray="60, 100"
            d="M18 2.0845
              a 15.9155 15.9155 0 0 1 0 31.831
              a 15.9155 15.9155 0 0 1 0 -31.831"
          />
          <text x="18" y="20.35" class="percentage">60%</text>
        </svg>
      </div>
      
       <div class="single-chart">
        <svg viewbox="0 0 36 36" class="circular-chart green">
          <path class="circle-bg"
            d="M18 2.0845
              a 15.9155 15.9155 0 0 1 0 31.831
              a 15.9155 15.9155 0 0 1 0 -31.831"
          />
          <path transform="scale(-1,1)" class="circle"
            stroke-dasharray="20, 100"
            d="M18 2.0845
              a 15.9155 15.9155 0 0 1 0 31.831
              a 15.9155 15.9155 0 0 1 0 -31.831"
          />
          <text x="18" y="20.35" class="percentage">-20%</text>
        </svg>
      </div>
    
    </div>