如何使用JavaScript和HTML5画布绘制条形图

在本教程中,我将向您展示如何使用JavaScript和HTML5画布作为使用条形图以图形方式显示数据的方法。

比起从头开始编写图表,使用现成的图表库更简便。但是,如果您想知道创建这样的库需要什么,本教程适合您。

什么是条形图?

条形图是用于表示数字数据的非常常用的工具。从财务报告到PowerPoint演示文稿再到信息图表,条形图非常常用,因为它们提供了易于理解的数字数据视图。

条形图使用条形图表示数字数据,条形图是宽度或高度与它们表示的数字数据成比例的矩形。

条形图有很多类型:

  • 水平和垂直条形图,具体取决于图表方向
  • 堆叠的条形图或经典条形图,用于表示多个数据系列
  • 2D或3D条形图
  • 等等

条形图的组成部分是什么?

让我们看一下构成条形图的各种类型的组件:

条形图的组成

  • 图表数据:这些是由图表表示的数字集和相关类别。
  • 数据系列的名称(1)。
  • 图表网格(2):提供参考系统,以便可以轻松理解视觉表示。
  • 条(3):彩色矩形,尺寸与所表示的数据成比例。
  • 图表图例(4):显示所用颜色与其代表的数据之间的对应关系。

现在,我们知道条形图的组成,让我们看看如何编写JavaScript代码以绘制这样的图表。

使用JavaScript绘制条形图

设置JS项目

要开始使用JavaScript和HTML5画布进行绘制,我们需要像这样设置我们的项目:

  • 创建一个文件夹来保存项目文件;我们叫这个文件夹 bar-chart-tutorial
  • 在项目文件夹中,创建一个文件并调用它 index.html。这将包含我们的HTML代码。
  • 同样在项目文件夹中,创建一个文件并调用它 script.js。这将包含用于绘制条形图的JavaScript代码。

我们将使事情变得非常简单,并在其中添加以下代码 index.html

1个
2
3
4
5
6
<html>
<body>
    <canvas id="myCanvas"></canvas>
    <script type="text/javascript" src="script.js"></script>
</body>
</html>

我们具有<canvas> 带有ID的 元素, myCanvas 以便我们可以在我们的JS代码中引用它。然后,我们通过<script> 标签加载JS代码 。

script.js 文件中添加以下代码 : 

1个
2
3
4
5
var myCanvas = document.getElementById("myCanvas");
myCanvas.width = 300;
myCanvas.height = 300;
  
var ctx = myCanvas.getContext("2d");

这将获得对canvas元素的引用,然后将width和height设置为300px。要在画布上绘制,我们只需要引用其2D上下文,其中包含所有绘制方法。

添加一些辅助功能

绘制条形图只需要知道如何绘制两个元素:

  • 绘制线:用于绘制网格线
  • 绘制一个彩色矩形:用于绘制图表的条形图

让我们为这两个元素创建辅助JS函数。我们将功能添加到 script.js 文件中。

1个
2
3
4
5
6
7
8
9
function drawLine(ctx, startX, startY, endX, endY,color){
    ctx.save();
    ctx.strokeStyle = color;
    ctx.beginPath();
    ctx.moveTo(startX,startY);
    ctx.lineTo(endX,endY);
    ctx.stroke();
    ctx.restore();
}

该 drawLine 函数具有六个参数:

  1. ctx:参考图纸上下文
  2. startX:线起点的X坐标
  3. startY:线起点的Y坐标
  4. endX:线终点的X坐标
  5. endY:线终点的Y坐标
  6. color:线的颜色

我们正在修改的颜色设置strokeStyle。这将确定用于绘制线条的颜色。我们使用ctx.save()和,ctx.restore()这样我们就不会影响此功能之外使用的颜色。

我们通过致电划清界限 beginPath()。这通知绘图上下文我们开始在画布上绘制一些新内容。我们用于 moveTo() 设置起点,调用 lineTo() 以指示终点,然后通过调用进行实际绘制 stroke()

我们需要的另一个辅助函数是绘制条的函数-这是一个彩色矩形。让我们将其添加到 script.js

1个
2
3
4
5
6
function drawBar(ctx, upperLeftCornerX, upperLeftCornerY, width, height,color){
    ctx.save();
    ctx.fillStyle=color;
    ctx.fillRect(upperLeftCornerX,upperLeftCornerY,width,height);
    ctx.restore();
}

该 drawBar 函数具有六个参数:

  1. ctx:参考图纸上下文
  2. upperLeftCornerX:栏左上角的X坐标
  3. upperLeftCornerY:栏左上角的X坐标
  4. width:条的宽度
  5. height:栏的高度
  6. color:栏的颜色

条形图数据模型

现在我们已经有了辅助功能,让我们继续图表的数据模型。所有类型的图表(包括条形图)背后都有数据模型。数据模型是一组结构化的数字数据。在本教程中,我们将使用一系列数据类别及其关联的数值,这些数值代表我按音乐类型分组的唱片集中的黑胶唱片数量:

  • 古典音乐:10
  • 替代摇滚:14
  • 流行音乐:2
  • 爵士乐:12

我们可以用对象形式在JavaScript中表示它。让我们将其添加到我们的 script.js文件中:

1个
2
3
4
5
6
var myVinyls = {
    "Classical music": 10,
    "Alternative rock": 14,
    "Pop": 2,
    "Jazz": 12
};

实施条形图组件

让我们实现将实际绘制条形图的组件。我们将通过在script.js文件中添加以下JavaScript对象来实现此目的 :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18岁
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
var Barchart = function(options){
    this.options = options;
    this.canvas = options.canvas;
    this.ctx = this.canvas.getContext("2d");
    this.colors = options.colors;
  
    this.draw = function(){
        var maxValue = 0;
        for (var categ in this.options.data){
            maxValue = Math.max(maxValue,this.options.data[categ]);
        }
        var canvasActualHeight = this.canvas.height - this.options.padding * 2;
        var canvasActualWidth = this.canvas.width - this.options.padding * 2;
 
        //drawing the grid lines
        var gridValue = 0;
        while (gridValue <= maxValue){
            var gridY = canvasActualHeight * (1 - gridValue/maxValue) + this.options.padding;
            drawLine(
                this.ctx,
                0,
                gridY,
                this.canvas.width,
                gridY,
                this.options.gridColor
            );
             
            //writing grid markers
            this.ctx.save();
            this.ctx.fillStyle = this.options.gridColor;
            this.ctx.font = "bold 10px Arial";
            this.ctx.fillText(gridValue, 10,gridY - 2);
            this.ctx.restore();
 
            gridValue+=this.options.gridScale;
        }
  
        //drawing the bars
        var barIndex = 0;
        var numberOfBars = Object.keys(this.options.data).length;
        var barSize = (canvasActualWidth)/numberOfBars;
 
        for (categ in this.options.data){
            var val = this.options.data[categ];
            var barHeight = Math.round( canvasActualHeight * val/maxValue) ;
            drawBar(
                this.ctx,
                this.options.padding + barIndex * barSize,
                this.canvas.height - barHeight - this.options.padding,
                barSize,
                barHeight,
                this.colors[barIndex%this.colors.length]
            );
 
            barIndex++;
        }
  
    }
}

该类首先存储 options 传递的参数。它存储 canvas 参考,并创建一个也作为类成员存储的图形上下文。然后,它存储colors 作为选项传递的 数组。

下一部分是最一致的 draw() 功能。这将通过首先绘制网格线,网格标记,然后使用通过options对象传递的参数绘制条形图来绘制图表 。

查看 draw()函数,我们可以看到,首先我们计算出数据模型的最大值。我们需要这个数字,因为我们将需要根据该值和画布的大小缩放所有条形图。否则,我们的栏可能会超出显示区域,因此我们不希望这样做。

canvasActualHeightcanvasActualWidth 变量存储在画布的使用经由通过填充的值来调整高度和宽度options。该padding 变量表示画布边缘和内部图表之间的像素数。 

然后,我们绘制图表的网格线。该 options.gridScale变量设置用于绘制线条的步骤。所以gridScale10的a 表示每10个单位绘制网格线。

要绘制网格线,我们使用helper函数 drawLine();至于网格线的颜色,我们取自 options.gridColor变量。请注意,画布坐标从0,0左上角开始, 并向右和向底部增加,而我们的网格值从底部到顶部逐渐增加。这就是为什么我们1 - gridValue/maxValue在公式中使用 了计算 gridY值的原因。

对于每条网格线,我们还在网格线上方2个像素处绘制网格线的值(这就是为什么gridY - 2 要使用文本的Y坐标的原因 )。

接下来,我们通过使用辅助函数来绘制条形图 drawBar()。计算每个条形的高度和宽度的数学方法非常简单。它考虑了图表数据模型中每个类别的填充以及值和颜色。

使用条形图组件

现在让我们看看如何使用Barchart上面实现的 类。我们需要实例化该类并调用该draw()函数。将以下代码添加到 script.js文件中:

01
02
03
04
05
06
07
08
09
10
11
var myBarchart = new Barchart(
    {
        canvas:myCanvas,
        padding:10,
        gridScale:5,
        gridColor:"#eeeeee",
        data:myVinyls,
        colors:["#a55ca5","#67b6c7", "#bccd7a","#eb9743"]
    }
);
myBarchart.draw();

该代码Barchart使用所需的选项创建该类的新实例 。index.html在浏览器中加载会 产生如下结果:

使用html5 canvas的简单条形图

添加数据系列名称和图表图例

要在图表下方添加数据系列名称,我们需要在绘制条形图script.js之后的文件中 添加以下代码 for-loop

01
02
03
04
05
06
07
08
09
10
...
//drawing series name
this.ctx.save();
this.ctx.textBaseline="bottom";
this.ctx.textAlign="center";
this.ctx.fillStyle = "#000000";
this.ctx.font = "bold 14px Arial";
this.ctx.fillText(this.options.seriesName, this.canvas.width/2,this.canvas.height);
this.ctx.restore(); 
...

我们还需要Barchart 像下面这样更改调用组件的方式:

01
02
03
04
05
06
07
08
09
10
11
12
var myBarchart = new Barchart(
    {
        canvas:myCanvas,
        seriesName:"Vinyl records",
        padding:20,
        gridScale:5,
        gridColor:"#eeeeee",
        data:myVinyls,
        colors:["#a55ca5","#67b6c7", "#bccd7a","#eb9743"]
    }
);
myBarchart.draw();

结果是这样的:

带有数据系列标题的条形图

要添加图例,我们首先需要修改 index.html为如下所示:

1个
2
3
4
5
6
7
<html>
<body>
    <canvas id="myCanvas" style="background: white;"></canvas>
    <legend for="myCanvas"></legend>
    <script type="text/javascript" src="script.js"></script>
</body>
</html>

该 legend标记将用作图表图例的占位符。该 for属性将图例链接到保存图表的画布。现在,我们需要添加创建图例的代码。我们将在index.js绘制数据系列名称的代码之后的文件中执行此操作 。该代码标识了legend与图表相对应的标签,并将添加来自图表数据模型的类别列表以及相应的颜色。生成的index.js文件将如下所示:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
var myCanvas = document.getElementById("myCanvas");
myCanvas.width = 300;
myCanvas.height = 300;
   
var ctx = myCanvas.getContext("2d");
 
function drawLine(ctx, startX, startY, endX, endY,color){
    ctx.save();
    ctx.strokeStyle = color;
    ctx.beginPath();
    ctx.moveTo(startX,startY);
    ctx.lineTo(endX,endY);
    ctx.stroke();
    ctx.restore();
}
 
function drawBar(ctx, upperLeftCornerX, upperLeftCornerY, width, height,color){
    ctx.save();
    ctx.fillStyle=color;
    ctx.fillRect(upperLeftCornerX,upperLeftCornerY,width,height);
    ctx.restore();
}
 
var myVinyls = {
    "Classical music": 10,
    "Alternative rock": 14,
    "Pop": 2,
    "Jazz": 12
};
 
var Barchart = function(options){
    this.options = options;
    this.canvas = options.canvas;
    this.ctx = this.canvas.getContext("2d");
    this.colors = options.colors;
  
    this.draw = function(){
        var maxValue = 0;
        for (var categ in this.options.data){
            maxValue = Math.max(maxValue,this.options.data[categ]);
        }
        var canvasActualHeight = this.canvas.height - this.options.padding * 2;
        var canvasActualWidth = this.canvas.width - this.options.padding * 2;
 
        //drawing the grid lines
        var gridValue = 0;
        while (gridValue <= maxValue){
            var gridY = canvasActualHeight * (1 - gridValue/maxValue) + this.options.padding;
            drawLine(
                this.ctx,
                0,
                gridY,
                this.canvas.width,
                gridY,
                this.options.gridColor
            );
             
            //writing grid markers
            this.ctx.save();
            this.ctx.fillStyle = this.options.gridColor;
            this.ctx.textBaseline="bottom";
            this.ctx.font = "bold 10px Arial";
            this.ctx.fillText(gridValue, 10,gridY - 2);
            this.ctx.restore();
 
            gridValue+=this.options.gridScale;
        }     
  
        //drawing the bars
        var barIndex = 0;
        var numberOfBars = Object.keys(this.options.data).length;
        var barSize = (canvasActualWidth)/numberOfBars;
 
        for (categ in this.options.data){
            var val = this.options.data[categ];
            var barHeight = Math.round( canvasActualHeight * val/maxValue) ;
            drawBar(
                this.ctx,
                this.options.padding + barIndex * barSize,
                this.canvas.height - barHeight - this.options.padding,
                barSize,
                barHeight,
                this.colors[barIndex%this.colors.length]
            );
 
            barIndex++;
        }
 
        //drawing series name
        this.ctx.save();
        this.ctx.textBaseline="bottom";
        this.ctx.textAlign="center";
        this.ctx.fillStyle = "#000000";
        this.ctx.font = "bold 14px Arial";
        this.ctx.fillText(this.options.seriesName, this.canvas.width/2,this.canvas.height);
        this.ctx.restore(); 
         
        //draw legend
        barIndex = 0;
        var legend = document.querySelector("legend[for='myCanvas']");
        var ul = document.createElement("ul");
        legend.append(ul);
        for (categ in this.options.data){
            var li = document.createElement("li");
            li.style.listStyle = "none";
            li.style.borderLeft = "20px solid "+this.colors[barIndex%this.colors.length];
            li.style.padding = "5px";
            li.textContent = categ;
            ul.append(li);
            barIndex++;
        }
    }
}
 
 
var myBarchart = new Barchart(
    {
        canvas:myCanvas,
        seriesName:"Vinyl records",
        padding:20,
        gridScale:5,
        gridColor:"#eeeeee",
        data:myVinyls,
        colors:["#a55ca5","#67b6c7", "#bccd7a","#eb9743"]
    }
);

最终结果将如下所示:

HTML5画布条形图最终结果

恭喜啦

我们已经看到使用HTML5画布绘制图表实际上并不难。它只需要一点数学和一点JavaScript知识。现在,您拥有绘制自己的条形图所需的一切。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值