1

I made a pieChart function thanks to the main CSS properties rotate:xdeg, border-radius:100% and overflow:hidden. That helped me to make rotating quarter pies of any size.

Here is the code:

function getRandomColor() {
  var letters = '0123456789ABCDEF'.split('');
  var color = '#';
  for (var i = 0; i < 6; i++) {
    color += letters[Math.floor(Math.random() * 16)];
  }
  return color;
} //http://stackoverflow.com/questions/1484506/random-color-generator-in-javascript

function drawQuarter(percent, angle, color) {
  var widthPercent = 90 - percent;
  var widthAngle = angle - 90 + percent;
  var output = '<div class="frame_0" style="transform: rotate(' + widthAngle + 'deg);">';
  output += '<div class="frame_1">';
  output += '<div class="frame_2" style="transform: rotate(' + widthPercent + 'deg);">';
  output += '<div class="square" style="background-color:' + color + '">';
  output += '</div>';
  output += '</div>';
  output += '</div>';
  output += '</div>';

  return output;
}

function pieChart(valuesArray) {
  var percent = [];
  var quarterQuantityArray = [];
  var HTMLoutput, dtop, dleft;
  var startAngle = 90;
  var rotation = startAngle;
  var output = '';
  var piechartSize = 40; // Half width of the piechart defined in css
  var pi = Math.PI;

  var sum = valuesArray.reduce(function(pv, cv) {
    return parseInt(pv) + parseInt(cv);
  }, 0); //http://stackoverflow.com/questions/3762589/fastest-javascript-summation

  if (sum == 0) //Case no data
  {
    HTMLoutput = '<div class="frame_square nodata"></div>';
    dtop = 30;
    dleft = 16;
    HTMLoutput += '<div class="label" style="top:' + dtop + 'px; left:' + dleft + 'px; line-height:normal;width:50px;">No data</div>';
    return HTMLoutput;
  } else { //Case with data

    for (var i = 0; i < valuesArray.length; i++) {
      percent[i] = valuesArray[i] / sum;
      quarterQuantityArray[i] = Math.ceil(percent[i] * 4);
    }

    for (var m = 0; m < valuesArray.length; m++) {
      var colorArray = ['#ef5350', '#66BB6A', '#26A69A'];
      var color = m > 2 ? getRandomColor() : colorArray[m];
      for (var j = 1; j <= quarterQuantityArray[m]; j++) {
        if (j != quarterQuantityArray[m]) {
          output += drawQuarter(90, rotation, color);
          rotation += 90;
        }
        if (j == quarterQuantityArray[m]) {
          var angle = percent[m] * 360 % 90;
          angle = angle == 0 ? 90 : angle; //In case of 25%, 50%, 75% and 100%
          output += drawQuarter(angle, rotation, color);
          rotation += angle;
        }
      }
    }

    var HTMLoutput = output;

    //Add labels
    //Case 100% for one value
    if (percent.indexOf(1) != -1) {
      dtop = piechartSize - 5;
      dleft = piechartSize - 10;
      HTMLoutput += '<div class="label" style="top:' + dtop + 'px; left:' + dleft + 'px; line-height:normal;">100%</div>';
    } else {
      var labelFrameSize = 10; //Half .piechart .label css size
      var label_int = piechartSize * 1 / 3; //retrait vers l'interieur du disque
      var rotation = (startAngle + 180) / 180;
      for (var n = 0; n < valuesArray.length; n++) {
      var deg = (rotation + percent[n]) * pi ;
        var labelY = piechartSize * (1 + Math.sin(deg)) - labelFrameSize - label_int * Math.sin(deg);
        var labelX = piechartSize * (1 + Math.cos(deg)) - labelFrameSize - label_int * Math.cos(deg);
        HTMLoutput += '<div class="label" style="top:' + labelY + 'px; left:' + labelX + 'px;">' + Math.round(percent[n] * 100, 0) + '%</div>';
        rotation += 2 * percent[n];
      }
    }
    return HTMLoutput;
  }
}

var test = pieChart(['6', '5', '4','8']);
document.getElementById('test').innerHTML = test;;
div.piechart,
.piechart .frame_0,
.piechart .frame_2 {
  width: 80px;
  height: 80px;
}

.piechart .frame_1,
.piechart .square {
  width: 40px;
  height: 40px;
}

div.piechart {
  position: relative;
  float: left;
  margin-right: 10px;
  text-align: center;
}

.piechart .frame_0 {
  position: absolute;
}

.piechart .frame_1 {
  overflow: hidden;
}

.piechart .square {
  border-radius: 100% 0 0 0;
}

.piechart .nodata {
  border-radius: 100%;
  background-color: #FFD419;
}

.piechart .label {
  position: absolute;
  text-align: center;
  width: 20PX;
  height: 20PX;
  line-height: 20PX;
  font-size: 7pt;
  color: white;
  font-weight: bold;
}
<div id="test" class="piechart">

</div>

The problem is that there are white lines between quarters and inside quarters larger than 25% that I'd like to be removed but I do not know how, has anybody got an idea ?

JSfiddle

fazac
  • 25
  • 4
  • If you want to give me some code optimization advice, please go here : codereview.stackexchange.com/questions/119209/css-js-piechart . Thanks for your help ! – fazac Feb 07 '16 at 20:29
  • I found a trick to solve the problem : I add a 0.01em border-bottom on .square divs. Updated JS here : http://jsfiddle.net/77vv08yt/8/ – fazac Feb 08 '16 at 12:51
  • You can post your solution as an answer and accept it. – xpy Feb 09 '16 at 09:33

1 Answers1

0

I found a trick to solve the problem : I add a 0.01em border-bottom on .square divs. Updated code here (it has been optimized since the first version) :

function getRandomColor() {
  var letters = '0123456789ABCDEF'.split('');
  var color = '#';
  for (var i = 0; i < 6; i++) {
    color += letters[Math.floor(Math.random() * 16)];
  }
  return color;
} //http://stackoverflow.com/questions/1484506/random-color-generator-in-javascript

function drawQuarter(percent, angle, color) {
  var widthPercent = 90 - percent;
  var widthAngle = angle - 90 + percent;
  var output = '<div class="frame_0" style="transform: rotate(' + widthAngle + 'deg);"><div class="frame_1"><div class="frame_2" style="transform: rotate(' + widthPercent + 'deg);"><div class="square" style="background-color:' + color + '; border-bottom:' + color + ' 0.01em solid;"></div></div></div></div>';

  return output;
}

function pieChart(valuesArray) {
  var percent = [];
  var quarterQuantityArray = [];
  var HTMLoutput, dtop, dleft;
  var startAngle = 90;
  var rotation = startAngle;
  var output = '';
  var piechartSize = 100; // Half width of the piechart defined in css
  var pi = Math.PI;

  var sum = valuesArray.reduce(function(pv, cv) {
    return parseInt(pv) + parseInt(cv);
  }, 0); //http://stackoverflow.com/questions/3762589/fastest-javascript-summation

  if (sum == 0) //Case no data
  {
    HTMLoutput = '<div class="frame_square nodata"></div>';
      dtop = piechartSize - 5;
      dwidth = 2*piechartSize;
    HTMLoutput += '<div class="label" style="top:' + dtop + 'px; width:' + dwidth + 'px; line-height:normal;">No data</div>';
    return HTMLoutput;
  } else { //Case with data

    for (var i = 0; i < valuesArray.length; i++) {
      percent[i] = valuesArray[i] / sum;
      quarterQuantityArray[i] = Math.ceil(percent[i] * 4);
    }

    for (var m = 0; m < valuesArray.length; m++) {
      var colorArray = ['#ef5350', '#66BB6A', '#26A69A'];
      var color = m > 2 ? getRandomColor() : colorArray[m];
      for (var j = 1; j <= quarterQuantityArray[m]; j++) {
        if (j != quarterQuantityArray[m]) {
          output += drawQuarter(90, rotation, color);
          rotation += 90;
        } else {
          var angle = percent[m] * 360 % 90;
          angle = angle == 0 ? 90 : angle; //In case of 25%, 50%, 75% and 100%
          output += drawQuarter(angle, rotation, color);
          rotation += angle;
        }
      }
    }

    var HTMLoutput = output;

    //Add labels
    //Case 100% for one value
    if (percent.indexOf(1) != -1) {
      dtop = piechartSize - 5;
      dleft = piechartSize - 10;
      HTMLoutput += '<div class="label" style="top:' + dtop + 'px; left:' + dleft + 'px; line-height:normal;">100%</div>';
    } else {
      var labelFrameSize = 10; //Half .piechart .label css size
      var label_int = piechartSize * 1 / 3; //retrait vers l'interieur du disque
      var rotation = (startAngle + 180) / 180;
      for (var n = 0; n < valuesArray.length; n++) {
      var deg = (rotation + percent[n]) * pi ;
        var labelY = piechartSize * (1 + Math.sin(deg)) - labelFrameSize - label_int * Math.sin(deg);
        var labelX = piechartSize * (1 + Math.cos(deg)) - labelFrameSize - label_int * Math.cos(deg);
        HTMLoutput += '<div class="label" style="top:' + labelY + 'px; left:' + labelX + 'px;">' + Math.round(percent[n] * 100, 0) + '%</div>';
        rotation += 2 * percent[n];
      }
    }
    return HTMLoutput;
  }
}

document.getElementById('test0').innerHTML = pieChart(['6', '5', '5','1']);
document.getElementById('test1').innerHTML = pieChart(['0', '0', '0']);
div.piechart,
.piechart .frame_0,
.piechart .frame_2,
.piechart .nodata {
  width: 200px;
  height: 200px;
}

.piechart .frame_1,
.piechart .square {
  width: 100px;
  height: 100px;
}

div.piechart {
  position: relative;
  float: left;
  margin-right: 10px;
  text-align: center;
}

.piechart .frame_0 {
  position: absolute;
}

.piechart .frame_1 {
  overflow: hidden;
}

.piechart .square {
  border-radius: 100% 0 0 0;
}

.piechart .nodata {
  border-radius: 100%;
  background-color: #FFD419;
}

.piechart .label {
  position: absolute;
  text-align: center;
  width: 20PX;
  height: 20PX;
  line-height: 20PX;
  font-size: 7pt;
  color: white;
  font-weight: bold;
}
<div id="test0" class="piechart"></div>
<div id="test1" class="piechart"></div>

I hope that anyone will find this free simple piechart usefull. But be certain it is usefull for, at least, myself...

fazac
  • 25
  • 4