7.1 创建饼图
饼图可能是最常用的数据可视化工具之一,因为它可以快速地为用户提供数据元素的相对权重。本节,我们将创建一个可配置的Pie Chart类,它接受一个数据元素的数组,并生成一个饼图。此外,我们将构造饼图的绘制方法,以这种方式,让饼图和标签将尽可能自动填满整个画布。
操作步骤
按照以下步骤,创建Pie Chart类,它能够根据一个数据的数组创建饼图,并自动定位,自动设置饼图及图例的尺寸:
1. 定义PieChart类的构造函数,来绘制饼图:
/* PieChart类的构造函数 */
function PieChart(canvasId, data){
//用户自定义属性
this.canvas = document.getElementById(canvasId);
this.data = data;
//常量定义
this.padding = 10;
this.legendBorder = 2;
this.pieBorder = 5;
this.colorLabelSize = 20;
this.borderColor = "#555";
this.shadowColor = "#777";
this.shadowBlur = 10;
this.shadowX = 2;
this.shadowY = 2;
this.font = "16pt Calibri";
//关系定义
this.context = this.canvas.getContext("2d");
this.legendWidth = this.getLegendWidth();
this.legendX = this.canvas.width - this.legendWidth;
this.legendY = this.padding;
this.pieAreaWidth = (this.canvas.width - this.legendWidth);
this.pieAreaHeight = this.canvas.height;
this.pieX = this.pieAreaWidth / 2;
this.pieY = this.pieAreaHeight / 2;
this.pieRadius = (Math.min(this.pieAreaWidth,
this.pieAreaHeight) / 2) - (this.padding);
// 绘制饼图
this.drawPieBorder();
this.drawSlices();
this.drawLegend();
}
2. 定义getLegendWidth()方法,该方法返回图例的宽度,计算宽度时会考虑图例文本的最大长度:
/* 根据图例文本的长度获取图例的宽度 */
PieChart.prototype.getLegendWidth = function(){
/* 遍历所有文本,并决定哪个文本是最长的一个,由它来决定图例的宽度 */
this.context.font = this.font;
var labelWidth = 0;
for (var n = 0; n < this.data.length; n++) {
var label = this.data[n].label;
labelWidth = Math.max(labelWidth, this.context. measureText(label).width);
}
return labelWidth + (this.padding * 2) + this.legendBorder + this.colorLabelSize;
};
3. 定义drawPieBorder()方法,该方法绘制饼图四周的边框:
PieChart.prototype.drawPieBorder = function(){
var context = this.context;
context.save();
context.fillStyle = "white";
context.shadowColor = this.shadowColor;
context.shadowBlur = this.shadowBlur;
context.shadowOffsetX = this.shadowX;
context.shadowOffsetY = this.shadowY;
context.beginPath();
context.arc(this.pieX, this.pieY, this.pieRadius + this. pieBorder, 0, Math.PI * 2, false);
context.fill();
context.closePath();
context.restore();
};
4. 定义drawSlices()方法,该方法遍历所有数据,并绘制每个数据在饼图中对应的小块:
/* 绘制饼图的小块 */
PieChart.prototype.drawSlices = function(){
var context = this.context;
context.save();
var total = this.getTotalValue();
var startAngle = 0;
for (var n = 0; n < this.data.length; n++) {
var slice = this.data[n];
// draw slice
var sliceAngle = 2 * Math.PI * slice.value / total;
var endAngle = startAngle + sliceAngle;
context.beginPath();
context.moveTo(this.pieX, this.pieY);
context.arc(this.pieX, this.pieY, this.pieRadius, startAngle, endAngle, false);
context.fillStyle = slice.color;
context.fill();
context.closePath();
startAngle = endAngle;
}
context.restore();
};
5. 定义getTotalValue()方法,该方法用于获取所有数据的总和:
/* 遍历数据数组,并把每个值相加,得到所有数据的总和 */
PieChart.prototype.getTotalValue = function(){
var data = this.data;
var total = 0;
for (var n = 0; n < data.length; n++) {
total += data[n].value;
}
return total;
};
6. 定义drawLegend()方法,该方法绘制图例:
/* 绘制图例 */
PieChart.prototype.drawLegend = function(){
var context = this.context;
context.save();
var labelX = this.legendX;
var labelY = this.legendY;
context.strokeStyle = "black";
context.lineWidth = this.legendBorder;
context.font = this.font;
context.textBaseline = "middle";
for (var n = 0; n < this.data.length; n++) {
var slice = this.data[n];
//绘制图例标签
context.beginPath();
context.rect(labelX, labelY, this.colorLabelSize, this. colorLabelSize);
context.closePath();
context.fillStyle = slice.color;
context.fill();
context.stroke();
context.fillStyle = "black";
context.fillText(slice.label, labelX + this.colorLabelSize + this.padding, labelY + this.colorLabelSize / 2);
labelY += this.colorLabelSize + this.padding;
}
context.restore();
};
7. 页面加载完成后,构建数据,并实例化一个PieChart对象:
window.onload = function(){
var data = [{
label: "Eating",
value: 2,
color: "red"
}, {
label: "Working",
value: 8,
color: "blue"
}, {
label: "Sleeping",
value: 8,
color: "green"
}, {
label: "Errands",
value: 2,
color: "yellow"
}, {
label: "Entertainment",
value: 4,
color: "violet"
}];
new PieChart("myCanvas", data);
};
8. 在HTML文档的body部分嵌入canvas标签:
<canvas id="myCanvas" width="600" height="300" style="border:1px solid black;">
</canvas>
相关参考
- 第1章 绘制圆弧
- 第1章 处理文本
- 第2章 绘制矩形
工作原理
在研究代码的工作原理之前,我们首先退一步想想PieChart应该做些什么。作为开发者,我们需要给对象传递一个画布ID,以便该对象知道在哪里绘制饼图,我们还需要数据元素的数组,以便该对象知道要绘制什么。
PieChart元素由drawSlices()和drawPieBorder()方法来渲染。drawSlices()方法执行以下步骤:
- 遍历数据元素
- 用每个数据占数据总和的份额乘以2π,计算出每个数据的角度
- 每个小块调用arc()方法绘制一个圆弧
- 使用数据元素提供的颜色,填充每个小块
一旦饼图渲染完成,我们就可以调用drawLegend()方法来绘制图例。drawPieBorder()方法执行以下步骤:
- 遍历数据元素
- 每个小块调用rect()方法绘制一个方框
- 使用数据元素提供的颜色,使用stroke()和fill()方法,对每个方框进行描边和填充
- 使用fillText()方法绘制每个元素对应的标签
页面加载完成后,我们可以创建一个数据元素的数组,该数据表明我们的日常活动中,每项活动的小时数。然后,传递数据元素的数组,实例化一个PieChart对象。
说明:
本节,我们以硬编码的方式,手动创建了一个数据元素的数组。然而,在现实生活中,数据更有可能由JSON 或 XML提供。