Javascript HTML5 Canvas 饼图

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6995797/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 00:11:54  来源:igfitidea点击:

HTML5 Canvas pie chart

javascriptcanvas

提问by Cameron

I'm attempting to create a simple pie chart like shown in the graphic below:

我正在尝试创建一个简单的饼图,如下图所示:

enter image description here

在此处输入图片说明

The chart will show the results for a quiz where a user can choose either a, b or c. They're 10 questions and the user can only choose one option per question.

该图表将显示测验的结果,用户可以选择 a、b 或 c。它们是 10 个问题,用户只能为每个问题选择一个选项。

What I want to do is show the pie chart with each segment being a percentage of 100% by passing in the values for either a,b, or c.

我想要做的是通过传入 a、b 或 c 的值来显示饼图,其中每个段都是 100% 的百分比。

I have the following so far:

到目前为止,我有以下几点:

var greenOne = "#95B524";
var greenTwo = "#AFCC4C";
var greenThree = "#C1DD54";

function CreatePieChart() {
  var chart = document.getElementById('piechart');
  var canvas = chart.getContext('2d');
  canvas.clearRect(0, 0, chart.width, chart.height);

  var total = 100;

  var a = 3;
  var b = 4;
  var c = 3;

  for (var i = 0; i < 3; i++) {
    canvas.fillStyle = "#95B524";
    canvas.beginPath();
    canvas.strokeStyle = "#fff";
    canvas.lineWidth = 3;
    canvas.arc(100, 100, 100, 0, Math.PI * 2, true);
    canvas.closePath();
    canvas.stroke();
    canvas.fill();
  }
}
CreatePieChart();
<canvas id="piechart" width="200" height="200"></canvas>

The colors are specific to the size of the segment, so green one is used for the largest and green three for the smallest.

颜色特定于段的大小,因此绿色的一个用于最大的,绿色的三个用于最小的。

Thanks

谢谢

回答by Alex W

Even after searching Google and triple-checking my radians values, etc. I was stillhaving trouble with this, so I have created a jsFiddlefor people to play with as a live example and will post the code below as well.

即使在谷歌搜索和三重检查我的弧度值等之后,我仍然遇到了这个问题,所以我创建了一个jsFiddle作为一个活生生的例子供人们玩,并将在下面发布代码。

var canvas = document.getElementById("can");
var ctx = canvas.getContext("2d");
var lastend = 0;
var data = [200, 60, 15]; // If you add more data values make sure you add more colors
var myTotal = 0; // Automatically calculated so don't touch
var myColor = ['red', 'green', 'blue']; // Colors of each slice

for (var e = 0; e < data.length; e++) {
  myTotal += data[e];
}

for (var i = 0; i < data.length; i++) {
  ctx.fillStyle = myColor[i];
  ctx.beginPath();
  ctx.moveTo(canvas.width / 2, canvas.height / 2);
  // Arc Parameters: x, y, radius, startingAngle (radians), endingAngle (radians), antiClockwise (boolean)
  ctx.arc(canvas.width / 2, canvas.height / 2, canvas.height / 2, lastend, lastend + (Math.PI * 2 * (data[i] / myTotal)), false);
  ctx.lineTo(canvas.width / 2, canvas.height / 2);
  ctx.fill();
  lastend += Math.PI * 2 * (data[i] / myTotal);
}
<canvas id="can" width="200" height="200" />

回答by zfrisch

I like the previous answer, but I felt it was lacking in code clarity and it didn't really cover how to utilize labels.

我喜欢之前的答案,但我觉得它缺乏代码清晰度,并且没有真正涵盖如何使用标签。

I moved the values into a data object array for easy declaration. Other values, like percentage, I explicitly declared as a property on the data object, or as a separate variable. This, I think, makes it easier to read.

我将值移动到数据对象数组中以便于声明。其他值,如百分比,我明确声明为数据对象的属性,或作为单独的变量。我认为,这使阅读更容易。

The refactoring also made it easier to tie the values to input boxes if that's something you're interested in.

如果您感兴趣,重构还可以更轻松地将值绑定到输入框。

To see what I mean and play with the values check out this CodePen: http://codepen.io/zfrisch/pen/pRbZeb

要了解我的意思并使用这些值,请查看此 CodePen:http://codepen.io/zfrisch/pen/pRbZeb

  var data = [{
    label: "one",
    value: 100,
    color: 'white'
  }, {
    label: "two",
    value: 100,
    color: 'skyBlue'
  }, {
    label: "three",
    value: 100,
    color: 'yellow'
  }];

  var total = 0;
  for (obj of data) {
    total += obj.value;
  }

  var canvas = document.getElementById('myCanvas');
  var ctx = canvas.getContext('2d');
  var previousRadian;
  var middle = {
    x: canvas.width / 2,
    y: canvas.height / 2,
    radius: canvas.height / 2,
  };
  
   //background
  ctx.beginPath();
  ctx.arc(middle.x, middle.y, middle.radius, 0, 2 * Math.PI);
  ctx.closePath();
  ctx.stroke();
  ctx.fillStyle = "black";
  ctx.fill();
   //end of background

  for (obj of data) {
    previousRadian = previousRadian || 0;
    obj.percentage = parseInt((obj.value / total) * 100)
    
    ctx.beginPath();
    ctx.fillStyle = obj.color;
    obj.radian = (Math.PI * 2) * (obj.value / total);
    ctx.moveTo(middle.x, middle.y);
    //middle.radius - 2 is to add border between the background and the pie chart
    ctx.arc(middle.x, middle.y, middle.radius - 2, previousRadian, previousRadian + obj.radian, false);
    ctx.closePath();
    ctx.fill();
    ctx.save();
    ctx.translate(middle.x, middle.y);
    ctx.fillStyle = "black";
    ctx.font = middle.radius / 10 + "px Arial";
    ctx.rotate(previousRadian + obj.radian);
    var labelText = "'" + obj.label + "' " + obj.percentage  + "%";
    ctx.fillText(labelText, ctx.measureText(labelText).width / 2, 0);
    ctx.restore();

    previousRadian += obj.radian;
  }
<canvas id="myCanvas" width="500" height="500" ></canvas>

回答by Akshat Maltare

I had the same problem before but I was able to solve this problem later.

我以前遇到过同样的问题,但后来我能够解决这个问题。

What I was missing was I was drawing an arch in context and was trying to fill it, due to which the color was spreading all across the circle because now the context was bound only between a radius line from center to the starting point of arch and the arch to bound the context.

我所缺少的是我在上下文中绘制了一个拱形并试图填充它,因此颜色在整个圆上蔓延,因为现在上下文仅在从中心到拱形起点的半径线之间绑定,并且绑定上下文的拱门。

But there was no other boundary the line from the end of arch to the center, as soon as I draw that line using the following:

但是,从拱形末端到中心的线没有其他边界,只要我使用以下内容绘制该线:

ctx.lineTo(center coordinates of circle);

I have a complete boundary of a pie, so now if I fill the color in context it will not get spread inside the whole circle but will be limited to that pie.

我有一个完整的馅饼边界,所以现在如果我在上下文中填充颜色,它不会散布在整个圆圈内,而是仅限于那个馅饼。