当前位置: 首页 > 文档资料 > HTML5 Canvas 实战 >

7.1 创建饼图

优质
小牛编辑
124浏览
2023-12-01

饼图可能是最常用的数据可视化工具之一,因为它可以快速地为用户提供数据元素的相对权重。本节,我们将创建一个可配置的Pie Chart类,它接受一个数据元素的数组,并生成一个饼图。此外,我们将构造饼图的绘制方法,以这种方式,让饼图和标签将尽可能自动填满整个画布。

创建饼图
图7-1 创建饼图

操作步骤

按照以下步骤,创建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()方法执行以下步骤:

  1. 遍历数据元素
  2. 用每个数据占数据总和的份额乘以2π,计算出每个数据的角度
  3. 每个小块调用arc()方法绘制一个圆弧
  4. 使用数据元素提供的颜色,填充每个小块

一旦饼图渲染完成,我们就可以调用drawLegend()方法来绘制图例。drawPieBorder()方法执行以下步骤:

  1. 遍历数据元素
  2. 每个小块调用rect()方法绘制一个方框
  3. 使用数据元素提供的颜色,使用stroke()和fill()方法,对每个方框进行描边和填充
  4. 使用fillText()方法绘制每个元素对应的标签

页面加载完成后,我们可以创建一个数据元素的数组,该数据表明我们的日常活动中,每项活动的小时数。然后,传递数据元素的数组,实例化一个PieChart对象。

说明:

本节,我们以硬编码的方式,手动创建了一个数据元素的数组。然而,在现实生活中,数据更有可能由JSON 或 XML提供。