HTML5 + JavaScript绘柱状图

骑猪看日落 2024-03-22 14:15 82阅读 0赞

之前用HTML5 + JavaScript绘柱状图,可以直观显示各类型产品或品牌的所占比例大小。详见:

HTML5 + JavaScript绘柱状图+1

现在需要针对每年获得各类品牌数据进行对比,绘制柱状图会更直观。

首先我们定义二维数组aBrandType,存放品牌类型及其对应的颜色:

  1. const aBrandType = new Array(
  2. //0:品牌名称,1:颜色
  3. ["区域公用品牌",'purple'], //0
  4. ["农业企业品牌",'blue'], //1
  5. ["农产品品牌", '#ff9900'], //2
  6. ["广西农交会金奖", '#339933'], //3
  7. ["广西农交会银奖", '#cc0000'],//4
  8. ["中国国际农交会金奖",'#00cccc']//5
  9. );

然后我们定义二维数组aBrands,存放每个年度获得各类品牌的数量:

  1. var aBrands = new Array(
  2. //0:年份,1:品牌总数量,2:区域公用品牌数量,3:农业企业数量;4:农产品品牌数量;5:广西农交会金奖数量……
  3. [2018,7,2,1,4],
  4. [2019,11,3,4,4],
  5. [2020,3,0,2,1,2],
  6. [2021,7,0,4,3],
  7. [2022,6,1,4,1]
  8. );

接着我们与绘制饼图时定义一个饼图对象相类似的,定义一个柱状图对象BarChart

  1. BarChart.prototype = {
  2. ……
  3. }//BarChart.prototype

柱状图对象BarChart具有如下公开属性:

  1. var bcBarChart = new BarChart({
  2. ctx: cbcCtx, //画布
  3. width: cbc.width,//画布宽度
  4. height: cbc.height,//画布高度
  5. data: aBrands,//柱状数据
  6. legend: aBrandType,//图例
  7. title: bcBarChartTitle //标题
  8. });

其中bcBarChartTitle是一个对象,包括以下属性:

  1. var bcBarChartTitle =
  2. {
  3. text:'品牌时间分布', //标题文字
  4. marginTop:25, //标题与画布顶部间距
  5. fontSize:20 //标题字体大小
  6. };

为了简便便起见,我们默认标题颜色为黑色,字体为微软雅黑,所以这里没有标题的颜色和字体属性。

在柱状图对象BarChart内部有几个方法。

第一个是初始化方法init

  1. init: function ()
  2. {
  3. this.scaleX = 60; //横坐标刻度间距
  4. this.scaleY = 28; //纵坐标刻度间距
  5. } //init

在初始化方法中,我们可以定义对象内部的一些属性。

第二个是绘制标题方法:

  1. //绘制标题
  2. drawTitle: function()
  3. {
  4. //如果标题属性已赋值且长度大于0,则输出标题文字
  5. if (typeof(this.title) != "undefined" && 0 < this.title.text.length)
  6. {
  7. this.ctx.font = "bolder " + this.title.fontSize + "px 微软雅黑";
  8. this.ctx.fillStyle = 'black';
  9. this.ctx.shadowBlur=20;
  10. this.ctx.shadowColor="gray";
  11. this.ctx.fillText(this.title.text, (this.width - this.title.text.length * this.title.fontSize)/2, this.title.marginTop);
  12. }//if
  13. },//drawTitle

第三个是绘制图例方法,包括绘制色块和对应的文字说明:

  1. //绘制图例
  2. drawLegend: function()
  3. {
  4. var maxLen = -1;
  5. var fontSize = 20;//字体大小
  6. var boxSize = 20;//色块尺寸
  7. var sb = 5; //间距Space between
  8. //取图例中文字说明字数的最大值
  9. this.legend.forEach(function (v)
  10. {
  11. if (v[1] > maxLen)
  12. {
  13. maxLen = v[1].length;
  14. }
  15. });
  16. taDbg.value += '\n maxLen=' + maxLen;
  17. // 文字说明的横坐标
  18. //boxX = this.width - maxLen * fontSize - legendBoxSize - sb;
  19. var textX = this.width - maxLen * fontSize - sb*2 - 200;
  20. //色块的的横坐标
  21. var boxX = textX - boxSize - sb;
  22. //纵坐标
  23. var y = this.title.marginTop + this.title.fontSize + sb;
  24. this.ctx.font = fontSize + "px 宋体";
  25. for (var i=0; i < this.legend.length; i++)
  26. {
  27. this.ctx.fillStyle = this.legend[i][1];
  28. this.ctx.fillRect(boxX, y, boxSize, boxSize);
  29. this.ctx.fillStyle = 'black';
  30. this.ctx.fillText(this.legend[i][0], textX, y + fontSize*3/4);
  31. y += sb*2 + boxSize;
  32. }//for
  33. },//drawLegend

第四个是绘制纵坐标方法:

  1. //绘制纵坐标
  2. drawOrdinate: function()
  3. {
  4. var fontSize = 20;
  5. var datumPointX = 30;//坐标原点x
  6. var datumPointY = this.height - 50;//坐标原点y
  7. //1、取纵坐标最大值
  8. var maxOrdinate = -1;
  9. this.data.forEach(function (v)
  10. {
  11. if (v[1] > maxOrdinate)
  12. {
  13. maxOrdinate = v[1];
  14. }
  15. });
  16. taDbg.value += '\n maxOrdinate=' + maxOrdinate;
  17. //画纵坐标轴
  18. this.ctx.moveTo(datumPointX, datumPointY);
  19. this.ctx.lineTo(datumPointX, this.title.fontSize + this.title.marginTop + fontSize);
  20. this.ctx.stroke();
  21. //画纵坐标刻度
  22. for (var i=0; i <= maxOrdinate; i++)
  23. {
  24. var j = datumPointY - i * this.scaleY;
  25. this.ctx.moveTo(datumPointX, j);
  26. //this.ctx.lineTo(this.width - 50 , j);
  27. this.ctx.lineTo(this.width - (this.data.length-1) * this.scaleX, j);
  28. this.ctx.stroke();
  29. }
  30. //绘制纵坐标刻度值
  31. //this.ctx.textAlign = "end";
  32. this.ctx.font = fontSize + "px 宋体";
  33. for (var i=1; i <= maxOrdinate; i++)
  34. {
  35. this.ctx.fillText(i, 0, datumPointY - i * this.scaleY + fontSize/3);
  36. }
  37. //输出纵坐标名称
  38. //this.ctx.fillText('数量', 0, datumPointY - maxOrdinate * this.scaleY - fontSize);
  39. this.ctx.fillText('数量', 0, datumPointY - maxOrdinate * this.scaleY - fontSize*2);
  40. //taDbg.value += '\n minOrdinate=' + minOrdinate + ' maxOrdinate=' + maxOrdinate;
  41. },//drawOrdinate

第五个是绘制横坐标方法:

  1. //绘制横坐标
  2. drawDiascissa: function()
  3. {
  4. var fontSize = 20;
  5. this.ctx.font = fontSize + "px 宋体";
  6. for (var i=0; i < this.data.length; i++)
  7. {
  8. this.ctx.fillText(this.data[i][0], (i+1) * this.scaleX , this.height - fontSize);
  9. }
  10. //this.ctx.fillText('年度', this.width - 4*fontSize, this.height - 50 + fontSize/3);
  11. this.ctx.fillText('年度', this.width - (this.data.length-1) * this.scaleX, this.height - fontSize);
  12. },//drawDiascissa

最后一个是绘制数据柱状条方法:

  1. //绘制数据柱状条
  2. drawBar: function()
  3. {
  4. var fontSize = 20;
  5. this.ctx.font = fontSize + "px 宋体";
  6. for (var i=0; i < this.data.length; i++)
  7. {
  8. var k = this.height - 50;
  9. for (var j=2; j < this.data[i].length; j++)
  10. {
  11. if ("undefined"!=this.data[i][j] && this.data[i][j] > 0)
  12. {
  13. this.ctx.fillStyle = this.legend[j-2][1];
  14. k -= this.data[i][j] * 28;
  15. this.ctx.fillRect((i+1) * this.scaleX, k, fontSize * 2, this.data[i][j] * this.scaleY);
  16. this.ctx.fillStyle = 'white';
  17. this.ctx.fillText(this.data[i][j], (i+1) * this.scaleX + Math.round(fontSize*2/3), k + this.scaleY*this.data[i][j]*2/3);
  18. //taDbg.value += '\n this.data[' + i + '][' + j + ']=' + this.data[i][j] + ' this.ctx.fillStyle=' + this.ctx.fillStyle;
  19. }//if
  20. }//for
  21. }
  22. },//drawBar

完整的代码如下:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="Author" content="PurpleEndurer">
  6. <title>柱状图</title>
  7. </head>
  8. <body>
  9. <canvas id="canvasBarChart" width="600" height="450" style="border:1px solid black; margin:50px; overflow:auto;"> 你的浏览器不支持HTML5 canvas </canvas>
  10. <textarea border="1" id="taDebug" cols="80" rows="15">debug:</textarea>
  11. <script type="text/javascript">
  12. var taDbg = document.getElementById("taDebug");
  13. const aBrandType = new Array(
  14. //0:品牌名称,1:颜色
  15. ["区域公用品牌",'purple'], //0
  16. ["农业企业品牌",'blue'], //1
  17. ["农产品品牌", '#ff9900'], //2
  18. ["广西农交会金奖", '#339933'], //3
  19. ["广西农交会银奖", '#cc0000'],//4
  20. ["中国国际农交会金奖",'#00cccc']//5
  21. );
  22. var aBrands = new Array(
  23. //0:年份,1:品牌总数量,2:区域公用品牌数量,3:农业企业数量;4:农产品品牌数量;5:广西农交会金奖数量……
  24. [2018,7,2,1,4],
  25. [2019,11,3,4,4],
  26. [2020,3,0,2,1,2],
  27. [2021,7,0,4,3],
  28. [2022,6,1,4,1]
  29. );
  30. var cbc = document.getElementById("canvasBarChart");
  31. cbc.height = 450;
  32. cbc.width = 600;
  33. var cbcCtx = cbc.getContext("2d");
  34. function BarChart(obj)
  35. {
  36. //导入外部公开的属性
  37. for(var key in obj)
  38. {
  39. this[key] = obj[key];
  40. }
  41. this.init();//初始化
  42. this.drawTitle();//绘制标题
  43. this.drawLegend();//绘制图例
  44. this.drawOrdinate();//绘制横坐标
  45. this.drawDiascissa();//绘制纵坐标
  46. this.drawBar();//绘制数据柱状条
  47. }
  48. BarChart.prototype = {
  49. //绘制纵坐标
  50. drawOrdinate: function()
  51. {
  52. //var scale = 28;//刻度
  53. var fontSize = 20;
  54. var datumPointX = 30;//坐标原点x
  55. var datumPointY = this.height - 50;//坐标原点y
  56. //1、取纵坐标最大值
  57. var maxOrdinate = -1;
  58. this.data.forEach(function (v)
  59. {
  60. if (v[1] > maxOrdinate)
  61. {
  62. maxOrdinate = v[1];
  63. }
  64. });
  65. taDbg.value += '\n maxOrdinate=' + maxOrdinate;
  66. //画纵坐标轴
  67. this.ctx.moveTo(datumPointX, datumPointY);
  68. this.ctx.lineTo(datumPointX, this.title.fontSize + this.title.marginTop + fontSize);
  69. this.ctx.stroke();
  70. //画纵坐标刻度
  71. for (var i=0; i <= maxOrdinate; i++)
  72. {
  73. var j = datumPointY - i * this.scaleY;
  74. this.ctx.moveTo(datumPointX, j);
  75. //this.ctx.lineTo(this.width - 50 , j);
  76. this.ctx.lineTo(this.width - (this.data.length-1) * this.scaleX, j);
  77. this.ctx.stroke();
  78. }
  79. //绘制纵坐标刻度值
  80. //this.ctx.textAlign = "end";
  81. this.ctx.font = fontSize + "px 宋体";
  82. for (var i=1; i <= maxOrdinate; i++)
  83. {
  84. this.ctx.fillText(i, 0, datumPointY - i * this.scaleY + fontSize/3);
  85. }
  86. //输出纵坐标名称
  87. //this.ctx.fillText('数量', 0, datumPointY - maxOrdinate * this.scaleY - fontSize);
  88. this.ctx.fillText('数量', 0, datumPointY - maxOrdinate * this.scaleY - fontSize*2);
  89. //taDbg.value += '\n minOrdinate=' + minOrdinate + ' maxOrdinate=' + maxOrdinate;
  90. },//drawOrdinate
  91. //绘制横坐标
  92. drawDiascissa: function()
  93. {
  94. var fontSize = 20;
  95. this.ctx.font = fontSize + "px 宋体";
  96. for (var i=0; i < this.data.length; i++)
  97. {
  98. this.ctx.fillText(this.data[i][0], (i+1) * this.scaleX , this.height - fontSize);
  99. }
  100. //this.ctx.fillText('年度', this.width - 4*fontSize, this.height - 50 + fontSize/3);
  101. this.ctx.fillText('年度', this.width - (this.data.length-1) * this.scaleX, this.height - fontSize);
  102. },//drawDiascissa
  103. //绘制数据柱状条
  104. drawBar: function()
  105. {
  106. var fontSize = 20;
  107. this.ctx.font = fontSize + "px 宋体";
  108. for (var i=0; i < this.data.length; i++)
  109. {
  110. var k = this.height - 50;
  111. for (var j=2; j < this.data[i].length; j++)
  112. {
  113. if ("undefined"!=this.data[i][j] && this.data[i][j] > 0)
  114. {
  115. this.ctx.fillStyle = this.legend[j-2][1];
  116. k -= this.data[i][j] * 28;
  117. this.ctx.fillRect((i+1) * this.scaleX, k, fontSize * 2, this.data[i][j] * this.scaleY);
  118. this.ctx.fillStyle = 'white';
  119. this.ctx.fillText(this.data[i][j], (i+1) * this.scaleX + Math.round(fontSize*2/3), k + this.scaleY*this.data[i][j]*2/3);
  120. //taDbg.value += '\n this.data[' + i + '][' + j + ']=' + this.data[i][j] + ' this.ctx.fillStyle=' + this.ctx.fillStyle;
  121. }//if
  122. }//for
  123. }
  124. },//drawBar
  125. //绘制图例
  126. drawLegend: function()
  127. {
  128. var maxLen = -1;
  129. var fontSize = 20;//字体大小
  130. var boxSize = 20;//色块尺寸
  131. var sb = 5; //间距Space between
  132. //取图例中文字说明字数的最大值
  133. this.legend.forEach(function (v)
  134. {
  135. if (v[1] > maxLen)
  136. {
  137. maxLen = v[1].length;
  138. }
  139. });
  140. taDbg.value += '\n maxLen=' + maxLen;
  141. // 文字说明的横坐标
  142. //boxX = this.width - maxLen * fontSize - legendBoxSize - sb;
  143. var textX = this.width - maxLen * fontSize - sb*2 - 200;
  144. //色块的的横坐标
  145. var boxX = textX - boxSize - sb;
  146. //纵坐标
  147. var y = this.title.marginTop + this.title.fontSize + sb;
  148. this.ctx.font = fontSize + "px 宋体";
  149. for (var i=0; i < this.legend.length; i++)
  150. {
  151. this.ctx.fillStyle = this.legend[i][1];
  152. this.ctx.fillRect(boxX, y, boxSize, boxSize);
  153. this.ctx.fillStyle = 'black';
  154. this.ctx.fillText(this.legend[i][0], textX, y + fontSize*3/4);
  155. y += sb*2 + boxSize;
  156. }//for
  157. },//drawLegend
  158. //绘制标题
  159. drawTitle: function()
  160. {
  161. //如果标题属性已赋值且长度大于0,则输出标题文字
  162. if (typeof(this.title) != "undefined" && 0 < this.title.text.length)
  163. {
  164. this.ctx.font = "bolder " + this.title.fontSize + "px 微软雅黑";
  165. this.ctx.fillStyle = 'black';//默认黑色
  166. this.ctx.shadowBlur=20;
  167. this.ctx.shadowColor="gray";
  168. this.ctx.fillText(this.title.text, (this.width - this.title.text.length * this.title.fontSize)/2, this.title.marginTop);
  169. }//if
  170. },//drawTitle
  171. //初始化
  172. init: function ()
  173. {
  174. this.scaleX = 60; //横坐标刻度间距
  175. this.scaleY = 28; //纵坐标刻度间距
  176. } //init
  177. }//BarChart.prototype
  178. var bcBarChartTitle =
  179. {
  180. text:'品牌时间分布', //标题文字
  181. marginTop:25, //标题与画布顶部间距
  182. fontSize:20 //标题字体
  183. };
  184. var bcBarChart = new BarChart({
  185. ctx: cbcCtx,
  186. width: cbc.width,
  187. height: cbc.height,
  188. data: aBrands,
  189. legend: aBrandType,
  190. title: bcBarChartTitle
  191. });
  192. </script>
  193. </body>
  194. </html>

代码显示效果如下图:

bda71004de6e46129e826eeca2a16068.png

发表评论

表情:
评论列表 (有 0 条评论,82人围观)

还没有评论,来说两句吧...

相关阅读