HTML5 Canvas 饼图

发布于 2024-11-28 19:01:51 字数 1122 浏览 0 评论 0原文

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

在此处输入图像描述

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

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

到目前为止我有以下内容:

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>

颜色取决于段的大小,因此绿色的一种用于最大的,绿色的三种用于最小的。

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.

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.

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

但可醉心 2024-12-05 19:01:51

即使在搜索 Google 并三次检查我的弧度值等之后,我仍然遇到了问题,所以我创建了一个 jsFiddle 供人们作为活生生的例子来玩,并将发布下面的代码。 (更新:在 fiddle v2 中,还添加了笔划和标签。)

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);
  ctx.arc(
    canvas.width / 2,  // x
    canvas.height / 2, // y
    canvas.height / 2, // radius
    lastend,           // startingAngle (radians)
    lastend + Math.PI * 2 * (data[i] / myTotal), // endingAngle (radians)
    false // antiClockwise (boolean)
  );
  ctx.lineTo(canvas.width / 2, canvas.height / 2);
  ctx.fill();
  lastend += Math.PI * 2 * (data[i] / myTotal);
}
<canvas id="can" width="200" height="200" />

Even after searching Google and triple-checking my radians values, etc. I was still having trouble with this, so I have created a jsFiddle for people to play with as a live example and will post the code below as well. (Update: in the fiddle v2, stroke and labels are added also.)

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);
  ctx.arc(
    canvas.width / 2,  // x
    canvas.height / 2, // y
    canvas.height / 2, // radius
    lastend,           // startingAngle (radians)
    lastend + Math.PI * 2 * (data[i] / myTotal), // endingAngle (radians)
    false // antiClockwise (boolean)
  );
  ctx.lineTo(canvas.width / 2, canvas.height / 2);
  ctx.fill();
  lastend += Math.PI * 2 * (data[i] / myTotal);
}
<canvas id="can" width="200" height="200" />

贩梦商人 2024-12-05 19:01:51

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

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

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

要了解我的意思并使用这些值,请查看此 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>

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

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>

心意如水 2024-12-05 19:01:51

这是一个不使用外部库、使用 html5 canvas 的饼图:

在此处输入图像描述

查看代码

但是最好使用库来绘制图表。在apex-charts中有一个名为sparkline的选项,它可以帮助您删除多余的东西并绘制最小且干净的图表。

这是使用 apex-charts 库的干净圆环图。 (使用 sparkline 选项删除额外的内容):

在此处输入图像描述

var options = {
  series: [620, 40],
  labels: ['Finished', 'Unfinished'],
  chart: {
    type: 'donut',
    sparkline: {
      enabled: true,
    }
  },
  plotOptions: {
    pie: {
      donut: {
        labels: {
          show: true,
          total: {
            showAlways: false,
            show: true,
            label: 'Total'
          }
        }
      }
    }
  },
};
var chart = new ApexCharts(document.querySelector("#chart"), options);
chart.render();

在 codepen 上查看

Here is a pie chart without using external libraries, using html5 canvas :

enter image description here

See the code

But it's better to use libraries for drawing charts. in apex-charts there is an option called sparkline, which helps you to remove the extra stuffs and draw a minimal and clean chart.

Here is a clean donut chart using apex-charts library. (Extra stuffs are removed with sparkline option):

enter image description here

var options = {
  series: [620, 40],
  labels: ['Finished', 'Unfinished'],
  chart: {
    type: 'donut',
    sparkline: {
      enabled: true,
    }
  },
  plotOptions: {
    pie: {
      donut: {
        labels: {
          show: true,
          total: {
            showAlways: false,
            show: true,
            label: 'Total'
          }
        }
      }
    }
  },
};
var chart = new ApexCharts(document.querySelector("#chart"), options);
chart.render();

See it on codepen

梦与时光遇 2024-12-05 19:01:51

我之前也遇到过同样的问题,但后来我解决了这个问题。

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

但是从拱门末端到中心的线没有其他边界,一旦我使用以下命令绘制该线:

ctx.lineTo(center coordinates of circle);

我有一个完整的馅饼边界,所以现在如果我在上下文中填充颜色,它将不会得到传播到整个圈子内,但仅限于那个饼。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文