Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scaleable css percentage ring

I'm trying to convert the code below basically into a scaleable version, I've tried using vw and vh, %values etc etc and I can't seem to get the right balance of values which work. Any help is appreciated.

This codepen might also help: http://codepen.io/anon/pen/dPNgvP

.arrow {
  position: relative;
  height: 0px;
  width: 0px;
  border-top: 18px solid #dd1111;
  border-left: 11px solid transparent;
  border-right: 11px solid transparent;
  position: absolute;
  bottom: 40px;
  left: 57px;
  z-index: 1;
  animation: load-arrow 1.6s linear;
  animation-fill-mode: forwards;
  -webkit-animation: load-arrow 1.6s linear;
  -webkit-animation-fill-mode: forwards;
}
@keyframes load-arrow {
  from {
    transform: translate(0, 0);
  }
  to {
    transform: translate(0, 55px);
  }
}
@-webkit-keyframes load-arrow {
  from {
    -webkit-transform: translate(0, 0);
  }
  to {
    -webkit-transform: translate(0, 55px);
  }
}
.pie {
  width: 140px;
  height: 140px;
  position: relative;
  border-radius: 140px;
  background-color: #DD1111;
  float: left;
  margin-right: 10px;
}
.pie .title {
  position: absolute;
  bottom: -40px;
  text-align: center;
  width: 100%;
}
.mask {
  position: absolute;
  width: 100%;
  height: 100%;
}
.pie1 .inner-right {
  transform: rotate(160deg);
  animation: load-right-pie-1 1s linear;
  -webkit-animation: load-right-pie-1 1s linear;
  -webkit-transform: rotate(160deg);
}
@keyframes load-right-pie-1 {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(160deg);
  }
}
@-webkit-keyframes load-right-pie-1 {
  from {
    -webkit-transform: rotate(0deg);
  }
  to {
    -webkit-transform: rotate(160deg);
  }
}
.outer-left {
  clip: rect(0px 70px 140px 0px);
}
.outer-right {
  clip: rect(0px 140px 140px 70px);
}
.inner-left {
  background-color: #710000;
  position: absolute;
  width: 100%;
  height: 100%;
  border-radius: 100%;
  clip: rect(0px 70px 140px 0px);
  transform: rotate(-180deg);
  -webkit-transform: rotate(-180deg);
}
.inner-right {
  background-color: #710000;
  position: absolute;
  width: 100%;
  height: 100%;
  border-radius: 100%;
  clip: rect(0px 70px 140px 0px);
  transform: rotate(180deg);
  -webkit-transform: rotate(180deg);
}
.content {
  width: 100px;
  height: 100px;
  border-radius: 50%;
  background-color: #fff;
  position: absolute;
  top: 20px;
  left: 20px;
  line-height: 100px;
  font-family: arial, sans-serif;
  font-size: 35px;
  text-align: center;
  z-index: 2;
}
.content span {
  opacity: 0;
  animation: load-content 3s;
  animation-fill-mode: forwards;
  animation-delay: 0.6s;
  -webkit-animation: load-content 3s;
  -webkit-animation-fill-mode: forwards;
  -webkit-animation-delay: 0.6s;
}
@keyframes load-content {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
@-webkit-keyframes load-content {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
<div class="pie pie1">
  <div class="title">Twitter</div>
  <div class="outer-right mask">
    <div class="inner-right"></div>
  </div>

  <div class="outer-left mask">
    <div class="inner-left"></div>
  </div>
  <div class="content">
    <span>44%</span>
  </div>
</div>
like image 874
Chris Connolly Avatar asked Jan 11 '15 22:01

Chris Connolly


1 Answers

You could simplify your code and make it dynamic using svg and JavaScript.

What this code does?

  1. Extract the percentage value from the text tag(s), calculates the angle and stores it for the animation.
  2. Animates the dark colored part from theta = 0 to the calculated angle(s).

Things you could do using this approach:

  1. You could dynamically set the percentage by simply changing the 44% in the text tags.
    • Valid formats ----> -%|--%|---%, Including float values.
  2. You could change the animation speed by changing the t variable.
  3. You could also make it responsive.

Responsive demo on CodePen ---> The width and height of the svg depends on the #container's width.

var dark = document.getElementById('dark'),
  t = 5,
  percentage = parseInt(document.getElementById('perc').innerHTML.slice(0, -1), 10),
  theta = 0,
  maxTheta = (180 * percentage) / 50,
  radius = document.getElementById('svg').getBBox().width / 2;
dark.setAttribute('transform', 'translate(' + radius + ',' + radius + ')');

var animate = setInterval(function() {
  theta += 0.5;
  var d = 'M0,0 v' + -radius + 'A' + radius + ',' + radius + ' 1 ' + ((theta > 180) ? 1 : 0) + ',1 ' + Math.sin(theta * Math.PI / 180) * radius + ',' + Math.cos(theta * Math.PI / 180) * -radius + 'z';
  dark.setAttribute('d', d);
  if (theta > maxTheta) {
    clearInterval(animate);
  }
}, t);
<svg id="svg" width="140" height="140" viewBox="0 0 141 141">
  <path id="light" d="M70,70 v-70 a70,70 0 0,1 0,140 a70,70 0 1,1 0,-140" fill="#DD1111" />
  <path id="dark" d="M70,70 v-70 a70,70 0 0,1 0,0" fill="#710000" />
  <path d="M20,70 a50,50 0 0,1 100,0 a50,50 0 0,1 -100,0" fill="white" />
  <text id="perc" x="70" y="79" font-size="30px" text-anchor="middle">44%</text>
</svg>

To put multiple percentage rings, you could use classes instead of ids. Again, you do not need to make any changes to the JavaScript code, simply change the percentage in the text tags.

enter image description here

Demo on CodePen

var dark = document.getElementsByClassName('dark'),
  radius = document.getElementsByClassName('svg')[0].getBBox().width / 2,
  t = 0.5,
  x = 0,
  y = 0,
  theta = {},
  maxTheta = calcTheta(document.getElementsByClassName('perc')),
  intervals = [];

function calcTheta(el) {
  var jbo = {};
  for (i = 0; i < el.length; i++) {
    theta[i] = 0;
    dark[i].setAttribute('transform', 'translate(' + radius + ',' + radius + ')');
    jbo[i] = (180 * parseInt(el[i].innerHTML.slice(0, -1), 10)) / 50;
  }
  return jbo;
}

var anim = function(j) {
  return function() {
    theta[j] += 0.5;
    var d = 'M0,0 v' + -radius + 'A' + radius + ',' + radius + ' 1 ' + ((theta[j] > 180) ? 1 : 0) + ',1 ' + Math.sin(theta[j] * Math.PI / 180) * radius + ',' + Math.cos(theta[j] * Math.PI / 180) * -radius + 'z';
    dark[j].setAttribute('d', d);
    if (theta[j] > maxTheta[j]) {
      clearInterval(intervals[j]);
    }
  }
};

for (var j = 0; j < dark.length; j++) {
  intervals.push(setInterval(anim(j), t));
}
#container {
  width: 100%;
}
.svg {
  display: inline-block;
  width: 16.5%;
}
<div id="container">
  <svg class="svg" viewBox="0 0 141 141">
    <path class="light" d="M70,70 v-70 a70,70 0 0,1 0,140 a70,70 0 1,1 0,-140" fill="#DD1111" />
    <path class="dark" d="M70,70 v-70 a70,70 0 0,1 0,0" fill="#710000" />
    <path d="M20,70 a50,50 0 0,1 100,0 a50,50 0 0,1 -100,0" fill="white" />
    <text class="perc" x="70" y="79" font-size="30px" text-anchor="middle">44%</text>
  </svg
  ><svg class="svg" viewBox="0 0 141 141">
    <path class="light" d="M70,70 v-70 a70,70 0 0,1 0,140 a70,70 0 1,1 0,-140" fill="#DD1111" />
    <path class="dark" d="M70,70 v-70 a70,70 0 0,1 0,0" fill="#710000" />
    <path d="M20,70 a50,50 0 0,1 100,0 a50,50 0 0,1 -100,0" fill="white" />
    <text class="perc" x="70" y="79" font-size="30px" text-anchor="middle">20%</text>
  </svg
  ><svg class="svg" viewBox="0 0 141 141">
    <path class="light" d="M70,70 v-70 a70,70 0 0,1 0,140 a70,70 0 1,1 0,-140" fill="#DD1111" />
    <path class="dark" d="M70,70 v-70 a70,70 0 0,1 0,0" fill="#710000" />
    <path d="M20,70 a50,50 0 0,1 100,0 a50,50 0 0,1 -100,0" fill="white" />
    <text class="perc" x="70" y="79" font-size="30px" text-anchor="middle">90%</text>
  </svg
  ><svg class="svg" viewBox="0 0 141 141">
    <path class="light" d="M70,70 v-70 a70,70 0 0,1 0,140 a70,70 0 1,1 0,-140" fill="#DD1111" />
    <path class="dark" d="M70,70 v-70 a70,70 0 0,1 0,0" fill="#710000" />
    <path d="M20,70 a50,50 0 0,1 100,0 a50,50 0 0,1 -100,0" fill="white" />
    <text class="perc" x="70" y="79" font-size="30px" text-anchor="middle">14%</text>
  </svg
  ><svg class="svg" viewBox="0 0 141 141">
    <path class="light" d="M70,70 v-70 a70,70 0 0,1 0,140 a70,70 0 1,1 0,-140" fill="#DD1111" />
    <path class="dark" d="M70,70 v-70 a70,70 0 0,1 0,0" fill="#710000" />
    <path d="M20,70 a50,50 0 0,1 100,0 a50,50 0 0,1 -100,0" fill="white" />
    <text class="perc" x="70" y="79" font-size="30px" text-anchor="middle">60%</text>
  </svg
  ><svg class="svg" viewBox="0 0 141 141">
    <path class="light" d="M70,70 v-70 a70,70 0 0,1 0,140 a70,70 0 1,1 0,-140" fill="#DD1111" />
    <path class="dark" d="M70,70 v-70 a70,70 0 0,1 0,0" fill="#710000" />
    <path d="M20,70 a50,50 0 0,1 100,0 a50,50 0 0,1 -100,0" fill="white" />
    <text class="perc" x="70" y="79" font-size="30px" text-anchor="middle">50%</text>
  </svg>
</div>
like image 120
Weafs.py Avatar answered Sep 17 '22 23:09

Weafs.py