怎样用JavaScript和HTML5 Canvas绘制图表
原文:https://code.tutsplus.com/zh-...
原作:John Negoita
翻译:Stypstive
在这篇教程中,我将展示用JavaScript和canvas作为手段,在饼状图和圆环图上显示数字信息。
与从零到一创建图表相比,其实有更简便的方式,例如用CodeCanyon上的这个图表库
但是如果你想知道库背后的原理,往下读。
什么是饼状图?
图表是用来图形化展示数据的工具。 饼状图将数据用切割成份的圆来展示。 每份的大小代表了数据值所代表的比例大小。
什么是圆环图?
简而言之,圆环图是饼状图的一个变种。 不同的是每份切向饼图的中心,这样只有轮缘是可见。 就这样,图表就如其名一个圆环。
开始用Canvas画
绘制饼图之前, 我们先看看它的组成部分。 我们先看看如何用canvas组件和JavaScript来画:
一条线
一个弧度(一个圆的部分)
一个颜色填充的图形
要想用HTML canvas画它,我们需要先创建几样东西:
创建一个项目文件夹,把它命名为piechart-tutorial
在piechart-tutorial文件夹中创建一个HTML文件index.html 这个文件中将是HTML代码。
一个JS文件scritp.js在piechart-tutorial文件夹中 这个文件中将是JavaScript代码。
我们将能简就简,然后添加一下代码到index.html中:
<html>
<body>
<canvas id="myCanvas"></canvas>
<script type="text/javascript" src="script.js"></script>
</body>
</html>
我们有一个ID为myCanvas的<canvas>元素。 然后我们通过<script>标签载入JS代码。
在script.js中,JS代码首先获取一个canvas的索引,然后设置canvas的宽和高。 要想在canvas上画,我们只需要一个2D上下文,哪里包含着所有的绘图方法。
var myCanvas = document.getElementById("myCanvas");
myCanvas.width = 300;
myCanvas.height = 300;
var ctx = myCanvas.getContext("2d");
现在我们已经设置了canvas的宽和高,同时也获取了canvas的索引,接下来我们定义一些画饼状图时,需要用到的可重用的函数。 我们将函数添加在script.js文件中。
function drawLine(ctx, startX, startY, endX, endY){
ctx.beginPath();
ctx.moveTo(startX,startY);
ctx.lineTo(endX,endY);
ctx.stroke();
}
函数drawLine接受5个参数:
ctx: 指向绘图上下文的索引startX:线段起始点的X坐标startY:线段起始点的Y坐标endX:线段结束点的X坐标endY:线段结束点的Y坐标
我们通过调用beginPath()来开始划线。 它通知绘图上下文,我们要在canvas上画一些东西了。 我们用moveTo()来设置起始点,调用lineTo()来表示结束点,然后调用stoke()来进行真正的绘图。
现在看看我们如何画圆的一部分,也叫做弧度。
function drawArc(ctx, centerX, centerY, radius, startAngle, endAngle){
ctx.beginPath();
ctx.arc(centerX, centerY, radius, startAngle, endAngle);
ctx.stroke();
}
函数drawArc接受6个参数:
ctx:指向绘图上下文的索引centerX:圆心的X坐标centerY:圆心的Y坐标radius: 圆的半径startAngle:部分圆的扇形的开始角度endAngle: 部分圆的扇形的结束角度
我们已经知道怎样画线和弧度了,现在让我看看如何画带颜色的形状。 由于我们的目标是画一份份组成的饼状图,所以我们创建一个画饼形图的份的函数。
function drawPieSlice(ctx,centerX, centerY, radius, startAngle, endAngle, color ){
ctx.fillStyle = color;
ctx.beginPath();
ctx.moveTo(centerX,centerY);
ctx.arc(centerX, centerY, radius, startAngle, endAngle);
ctx.closePath();
ctx.fill();
}
函数drawPieSlice接受7个参数:
ctx:指向绘图上下文的索引centerX:圆心的X坐标centerY:圆心的Y坐标radius:圆的半径startAngle:部分圆的扇形的起始角度endAngle:部分圆的扇形的结束角度color:填充的颜色
以下是调用这三个函数的例子:
drawLine(_ctx,100,100,200,200);
drawArc(_ctx, 150,150,150, 0, Math.PI/3);
drawPieSlice(_ctx, 150,150,150, Math.PI/2, Math.PI/2 + Math.PI/4, '#ff0000');
它将产生如下结果:
现在,我们有了画一个饼形图的所有必要的工具,让我们看看如何一起使用它们。
画饼形图
在概念上,任意图表都有两部分:
包含要展示的数据的数据模型。 这由特定类型的图表进行结构化。
图形化展示是指按照数学公式的规则,将数据模型中的数据通过视觉元素来进行展示。
饼状图数据模型
结构化饼状图数据的方式中,最常用的就是用一系列的类别和对应的值,每个类别的值与饼图的份相关联。
例如,饼图的数据模型展示按照流派进行分组,看起来就是下面这样:
古典音乐:10
另类摇滚:14
流行:2
爵士:12
我们可以在script.js文件中添加一个JS对象来存储数据模型,如下:
var myVinyls = {
"Classical music": 10,
"Alternative rock": 14,
"Pop": 2,
"Jazz": 12
};
饼状图的图形化展示
饼状图用圆来显示数据模型中的信息,通过将圆切分成一份份。 每份和数据模型中的类别对应,每份的大小与对应类别的值成正比。
我的音乐集有四个类别。 每个类别在饼形图中的份数大小,与它的类别对应的值成正比。
但是我们怎样度量份数的大小? 简单--我们通过每份的角度。 我们所需要知道的就是它占360度或者2PI的份数。 那么半圆就是180deg或者PI,1/4圆90度或PI/2,以此类推。
为了决定每个类别的份的角度,我们用以下公式:
份角度= 2 * PI * 类别值 / 总值
按照这个公式,古典音乐的那份近似得到如下角度。 0.526 * PI 或者 94度
让我开始画吧。 这次我们将用JavaScript类,将其命名为 PieChart 构造函数接受options做为参数,options包含以下:
canvas:指向我们想要画饼状图的索引
data:盛放数据模型的对象的索引
colors:一个数组,数组中是每份的颜色。
PieChart类同时也包含一个draw()方法,它来对图表进行实际的绘制。
var Piechart = function(options){
this.options = options;
this.canvas = options.canvas;
this.ctx = this.canvas.getContext("2d");
this.colors = options.colors;
this.draw = function(){
var total_value = 0;
var color_index = 0;
for (var categ in this.options.data){
var val = this.options.data[categ];
total_value += val;
}
var start_angle = 0;
for (categ in this.options.data){
val = this.options.data[categ];
var slice_angle = 2 * Math.PI * val / total_value;
drawPieSlice(
this.ctx,
this.canvas.width/2,
this.canvas.height/2,
Math.min(this.canvas.width/2,this.canvas.height/2),
start_angle,
start_angle+slice_angle,
this.colors[color_index%this.colors.length]
);
start_angle += slice_angle;
color_index++;
}
}
}
类中首先将传入的options参数进行存储。 它保存了canvas的索引,同时也创建一个绘画上下文作为类成员变量。 然后它存储了options中的colors数组。
接下来的部分是最根本的,函数draw()。 它会从数据模型中提取数据。 首先,它计算数据模型中所有数据值的和。 然后对其中每个类别应用上面提到的计算角度的函数。 最后我们调用drawPieSlice()函数,用canvas的中心作为饼状图的中心。 至于半径,我们用canvas宽度的一半与canvas高度的一半的较小值,因为我们不想让饼状图超出canvas。
同样,每次画一个类别时,要偏移每份的起始角度和结束角度,否则会发生重叠。
要想使用类,我们必须先创建一个实例对象,然后在创建的对象上调用draw()方法。
var myPiechart = new Piechart(
{
canvas:myCanvas,
data:myVinyls,
colors:["#fde23e","#f16e23", "#57d9ff","#937e88"]
}
);
myPiechart.draw();
结果看起来如下这样:
绘制圆环图
我们已经看到如何创建饼状图。 同样我们看到,圆环图与饼状图不同之处仅在于中间多了一个洞。 怎样画洞呢? 我们可以画一个白色的圆在饼状图上。
让我们通过修改PieChart类来做它。
var Piechart = function(options){
this.options = options;
this.canvas = options.canvas;
this.ctx = this.canvas.getContext("2d");
this.colors = options.colors;
this.draw = function(){
var total_value = 0;
var color_index = 0;
for (var categ in this.options.data){
var val = this.options.data[categ];
total_value += val;
}
var start_angle = 0;
for (categ in this.options.data){
val = this.options.data[categ];
var slice_angle = 2 * Math.PI * val / total_value;
drawPieSlice(
this.ctx,
this.canvas.width/2,
this.canvas.height/2,
Math.min(this.canvas.width/2,this.canvas.height/2),
start_angle,
start_angle+slice_angle,
this.colors[color_index%this.colors.length]
);
start_angle += slice_angle;
color_index++;
}
//drawing a white circle over the chart
//to create the doughnut chart
if (this.options.doughnutHoleSize){
drawPieSlice(
this.ctx,
this.canvas.width/2,
this.canvas.height/2,
this.options.doughnutHoleSize * Math.min(this.canvas.width/2,this.canvas.height/2),
0,
2 * Math.PI,
"#ff0000"
);
}
}
}
添加的代码在options参数中,通过一个doughnutHoleSize成员变量。 如果这个参数在options中没有传,代码就按照之前的进行绘制,如果传了,就在饼状图中心画一个白色的圆形。
圆的半径由饼形图的半径和doughnutHoleSize参数的乘积来决定。 它应该是0到1之间的数字,0代表饼形图,大于0时,值越大饼形图中间的洞越大,当值为1时会使图表不可见。
要想画一个图表一半大小的圆环图,我们可以将doughnutHoleSize设置为0.5,然后像下面这样调用:
var myDougnutChart = new Piechart(
{
canvas:myCanvas,
data:myVinyls,
colors:["#fde23e","#f16e23", "#57d9ff","#937e88"],
doughnutHoleSize:0.5
}
);
myDougnutChart.draw();
以下是结果:
添加标签和图表图例
我们的饼状图表和圆环图表看起来挺棒了,但是它会变得更棒,通过添加两样东西:
值标签:显示每份对应的百分比
图表图例:显示图表中每个类别和颜色的对应关系
通常,每份的值用百分比来表示,通过100 * 每份值 / 总的值来计算,整个圆代表100%。
例如,在我们的例子数据中,古典音乐可以近似地用26%来表示。 如果能将这个值刚好显示在对应的份上面就太好了。 要想这样,我们可以用绘图上下文的fillText(text, x, y)函数。 这个函数接受三个参数:文本和x,y坐标。
怎样计算放置文本的x和y坐标呢? 我们必须动用一些几何知识了,一个叫做极坐标的东西。 一般地,极坐标用半径和角度来定义一个点的位置。 我们将要用到的两个公式是:
x = R * cos(angle)
y = R * sin(angle)
我们将要应用这个公式,将文本放在饼状图的半径的一半位置与每份角度的一半位置处。 要想做它,我们需要修改我们的PieChart类,增加如下代码在if(this.options.doughnutHoleSize){...}代码块中。
...
start_angle = 0;
for (categ in this.options.data){
val = this.options.data[categ];
slice_angle = 2 * Math.PI * val / total_value;
var pieRadius = Math.min(this.canvas.width/2,this.canvas.height/2);
var labelX = this.canvas.width/2 + (pieRadius / 2) * Math.cos(start_angle + slice_angle/2);
var labelY = this.canvas.height/2 + (pieRadius / 2) * Math.sin(start_angle + slice_angle/2);
if (this.options.doughnutHoleSize){
var offset = (pieRadius * this.options.doughnutHoleSize ) / 2;
labelX = this.canvas.width/2 + (offset + pieRadius / 2) * Math.cos(start_angle + slice_angle/2);
labelY = this.canvas.height/2 + (offset + pieRadius / 2) * Math.sin(start_angle + slice_angle/2);
}
var labelText = Math.round(100 * val / total_value);
this.ctx.fillStyle = "white";
this.ctx.font = "bold 20px Arial";
this.ctx.fillText(labelText+"%", labelX,labelY);
start_angle += slice_angle;
}
...
上面代码遍历每份,计算百分比和位置,然后调用fillText()方法将之绘制到图表上。 我们用了fillStyle属性来设置文本颜色为白色,font属性来设置标签文本的字体、样式和大小。 同样重要,需要注意的是圆环图的doughnutHoleSize设置后,标签会被往边沿推,以使文本能处于圆环图每份的中央。
以下就是带值标签的图表看起来的样子:
要想完成图表,最后一件事就是为图表添加图例。 我们的图表图例将会显示数据模型中数据的类别和对应每份的颜色。 首先,我们需要对index.html文件做些修改,添加一个<div>标签用来存储我们的图例元素。
<html>
<body>
<canvas id="myCanvas"></canvas>
<div id="myLegend"></div>
<script type="text/javascript" src="script.js"></script>
</body>
</html>
然后在script.js中,我们添加创建图例元素的代码。 我们将代码添加在PieChart类中draw()函数的末尾。
...
if (this.options.legend){
color_index = 0;
var legendHTML = "";
for (categ in this.options.data){
legendHTML += "<div><span style='display:inline-block;width:20px;background-color:"+this.colors[color_index++]+";'> </span> "+categ+"</div>";
}
this.options.legend.innerHTML = legendHTML;
}
...
代码通过传入options参数来寻找legend元素。 如果找到,就在其中填上带颜色的块和数据模型类别的名字。
同时,我们也需要将调用绘值图表的代码改成如下形式:
var myLegend = document.getElementById("myLegend");
var myDougnutChart = new Piechart(
{
canvas:myCanvas,
data:myVinyls,
colors:["#fde23e","#f16e23", "#57d9ff","#937e88"],
legend:myLegend
}
);
myDougnutChart.draw();
这就是结果的图表和图表图例:
恭喜
我们看到用HTML5 canvas绘制图表,其实也并不是那么困难。 它仅仅需要一点数学和JavaScript知识。 你现在有了要画一个你自己的饼形图和圆环图的全部。
如果你想要一个简便快捷的解决方案,用来创建饼形图和圆环图,同时还有其他类型的图表。你可以下载信息图表和HTML图表标签库或者WordPress插件对应的Charts and Graphs WordPress Visual Designer。
关于Envato艺云台
Envato艺云台是数据资产和创造性人才汇聚的全球领先市场平台。全球数百万人都选择通过我们的市场平台、工作室和课程来购买文件、选聘自由职业者,或者学习创建网站、制作视频、应用、制图等所需的技能。我们的子网站包括Envato艺云台Tuts+ 网络,全球最大的H5、PS、插图、代码和摄影教程资源库,以及Envato艺云台市场,其中的900多万类数字资产均通过以下七大平台进行销售 - CodeCanyon、ThemeForest、GraphicRiver、VideoHive、PhotoDune、AudioJungle和3DOcean。
怎样用JavaScript和HTML5 Canvas绘制图表的更多相关文章
- 学习笔记:HTML5 Canvas绘制简单图形
HTML5 Canvas绘制简单图形 1.添加Canvas标签,添加id供js操作. <canvas id="mycanvas" height="700" ...
- 使用 HTML5 Canvas 绘制出惊艳的水滴效果
HTML5 在不久前正式成为推荐标准,标志着全新的 Web 时代已经来临.在众多 HTML5 特性中,Canvas 元素用于在网页上绘制图形,该元素标签强大之处在于可以直接在 HTML 上进行图形操作 ...
- 使用html5 canvas绘制图片
注意:本文属于<html5 Canvas绘制图形入门详解>系列文章中的一部分.如果你是html5初学者,仅仅阅读本文,可能无法较深入的理解canvas,甚至无法顺畅地通读本文.请点击上述链 ...
- 使用html5 canvas绘制圆形或弧线
注意:本文属于<html5 Canvas绘制图形入门详解>系列文章中的一部分.如果你是html5初学者,仅仅阅读本文,可能无法较深入的理解canvas,甚至无法顺畅地通读本文.请点击上述链 ...
- html5 Canvas绘制图形入门详解
html5,这个应该就不需要多作介绍了,只要是开发人员应该都不会陌生.html5是「新兴」的网页技术标准,目前,除IE8及其以下版本的IE浏览器之外,几乎所有主流浏览器(FireFox.Chrome. ...
- 纯JavaScript实现HTML5 Canvas六种特效滤镜
纯JavaScript实现HTML5 Canvas六种特效滤镜 小试牛刀,实现了六款简单常见HTML5 Canvas特效滤镜,并且封装成一个纯 JavaScript可调用的API文件gloomyfi ...
- 使用html5 Canvas绘制线条(直线、折线等)
使用html5 Canvas绘制直线所需的CanvasRenderingContext2D对象的主要属性和方法(有"()"者为方法)如下: 属性或方法 基本描述 strokeSty ...
- 解决html5 canvas 绘制字体、图片与图形模糊问题
html5 canvas 绘制字体.图片与图形模糊问题 发生情况 多出现在高dpi设备,这意味着每平方英寸有更多的像素,如手机,平板电脑.当然很多高端台式电脑也有高分辨率高dpi的显示器. canva ...
- html5 canvas绘制环形进度条,环形渐变色仪表图
html5 canvas绘制环形进度条,环形渐变色仪表图 在绘制圆环前,我们需要知道canvas arc() 方 ...
随机推荐
- 通过IP访问公司公共资源库(共享文件)
今天,公司发通知说公司内部共享资源库已搭建完成,给了一个IP地址说可以访问了,那么如何去查看其他电脑的共享文件,下面以Windows7为例进行说明: 1:点击开始-运行(如图),或者快捷键(Win+R ...
- MM32F0140 GPIO驱动LED灯(MM32F0140 GPIO)
目录: 1.MM32F0140简介 2.MM32F0140系统时钟配置 3.MM32F0140的GPIO外设配置及其初始化 4.使用官网的Systick定时器做延时 5.MM32F0140 GPIO驱 ...
- Zookeeper(1)-安装与基础使用
Zookeeper 服务端 工作机制 Zookeeper从设计模式角度来理解:是一个基于观察者模式设计的分布式服务管理框架,它负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发 ...
- props配置
配置项props 功能:让组件接收外部传过来的数据[相当于微信转账:有一个转账人转钱给接收者,接收者需要确认接收] (1)传递数据: <Demo name="xxx"> ...
- 题解 P1659 【[国家集训队]拉拉队排练】
一眼可得PAM 如果没学过PAM的可以看这里:PAM学习小结 我们令PAM上多记录一个信息\(sum\),表示该节点表示串在原串上出现了多少次. 当我们处理完了\(sum\),对于长度\(len\)为 ...
- LGP3349口胡
建议改为:如何使用FWT直接把反演题草过去 需要清楚 FWT 的本质是什么. 首先我们有一个明显的 DP: 设 \(dp[u][x][S]\) 代表 \(u\) 在图中为 \(x\),子树包含集合 \ ...
- Android 12(S) 图形显示系统 - BufferQueue的工作流程(十)
题外话 疫情隔离在家,周末还在努力学习的我 ..... 一.前言 上一篇文章中,有基本讲清楚Producer一端的处理逻辑,最后也留下了一个疑问: Consumer是什么时候来消费数据的?他是自己主 ...
- collections 数据类型扩展模块
在内置数据类型(dict.list.set.tuple)的基础上,collections模块还提供了几个额外的数据类型:Counter.deque.defaultdict.namedtuple和Ord ...
- 一个最简单的Dubbo入门框架
Dubbo背景和简介 Dubbo开始于电商系统,因此在这里先从电商系统的演变讲起. 1.单一应用框架(ORM) 当网站流量很小时,只需一个应用,将所有功能如下单支付等都部署在一起,以减少部署节点和成本 ...
- JNDI With RMI
JNDI With RMI JNDI with RMI JNDI即Java Naming and Directory Interface(JAVA命名和目录接口),jndi类似于一个索引中心,允许客户 ...