1

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

Temani Afif
  • 245,468
  • 26
  • 309
  • 415
Seth Killian
  • 908
  • 9
  • 20

1 Answers1

1

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>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • Is it possible for the -50% to go in the other direction though, as if the orange was going counterclockwise? So if it's negative, it should be like a mirror image of a positive version. – Seth Killian Feb 03 '18 at 21:32
  • @SethKillian so you mean for the 50% is start from the bottom and go to top form the left ? ... the -20% is also wrong in this case – Temani Afif Feb 03 '18 at 21:33
  • For negative values it should start from the top and go to the left. For positive values, it should start from the top and go to the right. I added an image of the desired result in the question. – Seth Killian Feb 03 '18 at 21:38
  • @SethKillian yes exactly what i did no ? :) i have two negative values in my example going from top to left and one positive from top to right – Temani Afif Feb 03 '18 at 21:42
  • In the snippet, it looks like they're all going to the right. Are you seeing something different? – Seth Killian Feb 03 '18 at 21:46
  • @SethKillian which browser you are using ?, am in Chrome right now – Temani Afif Feb 03 '18 at 21:47
  • I'm in Safari - Let me try it again in Chrome. – Seth Killian Feb 03 '18 at 21:48
  • Ok great, it works exactly as you said in Chrome! Is there any way to make it compatible in safari too? – Seth Killian Feb 03 '18 at 21:49
  • @SethKillian yes am invesitgating to see what the issue ;) – Temani Afif Feb 03 '18 at 21:49
  • @SethKillian the animation is the issue, i don't know exacly why but if you remove it you will have the correct direction .. and this is not working with Firferox also, very strange – Temani Afif Feb 03 '18 at 21:55
  • Interesting. Thanks for the help! – Seth Killian Feb 03 '18 at 22:00
  • Reliable cross-browser animation of SVG sub elements is only possible with JavaScript. MSFT does not support SMIL, and only Chrome supports SVG2-style animations so far. – Michael Mullany Feb 04 '18 at 00:38
  • @MichaelMullany sure but what is strange is why the animation is not simply ignored and we have a correct visual with the rotation which well supported i guess [if we omit the animationwe have a correct shape and the rotation work fine] ... by the way i find another idea that will work fine ;) – Temani Afif Feb 04 '18 at 08:02
  • @SethKillian well i endup finding another idea which doesn't need rotation animation as it's still not yet supported well .. you may check it if still intrested :) – Temani Afif Feb 04 '18 at 08:03
  • If you want your first answer to work in Firefox, stop mixing attribute and CSS transforms. I.e. change transform="rotate(-180)" to style="transform:rotate(-180deg)" etc. – Robert Longson Feb 04 '18 at 08:16
  • @RobertLongson yes i also tried this but for some reson it's not well rendred in firefox :s ... check again the first snippet it's updated. The circles aren't overlapping correctly – Temani Afif Feb 04 '18 at 08:20
  • What if I want several percentages in the same circle? – lrente Oct 28 '19 at 10:22
  • @user1835591 what do you mean by *several percentages*? – Temani Afif Oct 28 '19 at 12:58