canvas图表详解系列(1):柱状图
本章建议学习时间4小时
学习方式:详细阅读,并手动实现相关代码(如果没有canvas基础,需要先学习前面的canvas基础笔记)
学习目标:此教程将教会大家如何使用canvas绘制各种图表,详细分解步骤,本次讲解柱状图。
源文件下载地址:https://github.com/sutianbinde/charts
柱状图
柱状图是前端最基本的图表之一,我们的案例展示效果如下

功能:横轴年份,纵轴产量,图表会根据年份的多少自动分配柱的宽度,高度会有由低到高的运动效果,当鼠标移入时,当前柱会颜色加深。点击图表会有刷新重载动画效果。
实现步骤
--新建Html文件,写入canvas标签,并且定义绘制图表的方法
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<canvas id="barChart" height="400" width="600" style="margin:50px"> 你的浏览器不支持HTML5 canvas </canvas> <script type="text/javascript">
//封装绘制图表的方法
function goBarChart(dataArr){ } //调用方法,并传入需要显示的数据
goBarChart(
[[2007, 750], [2008, 425], [2009, 960], [2010, 700], [2011, 800], [2012, 975], [2013, 375], [2014, 775]]
)
</script>
</body>
</html>
--在 goBarChart方法中定义需要使用的变量 并获取 canvas上下文
// 声明所需变量
var canvas,ctx;
// 图表属性
var cWidth, cHeight, cMargin, cSpace;
var originX, originY;
// 柱状图属性
var bMargin, tobalBars, bWidth, maxValue;
var totalYNomber;
var gradient; // 运动相关变量
var ctr, numctr, speed;
//鼠标移动
var mousePosition = {}; // 获得canvas上下文
canvas = document.getElementById("barChart");
if(canvas && canvas.getContext){
ctx = canvas.getContext("2d");
}
--初始化图表 (接着上一步的代码写在 goBarChart方法中 )
initChart(); // 图表初始化
// 图表初始化
function initChart(){
// 图表信息
cMargin = 30;
cSpace = 60;
cHeight = canvas.height - cMargin*2 - cSpace;
cWidth = canvas.width - cMargin*2 - cSpace;
originX = cMargin + cSpace;
originY = cMargin + cHeight;
// 柱状图信息
bMargin = 15;
tobalBars = dataArr.length;
bWidth = parseInt( cWidth/tobalBars - bMargin );
maxValue = 0;
for(var i=0; i<dataArr.length; i++){
var barVal = parseInt( dataArr[i][1] );
if( barVal > maxValue ){
maxValue = barVal;
}
}
maxValue += 50;
totalYNomber = 10;
// 运动相关
ctr = 1;
numctr = 100;
speed = 10;
//柱状图渐变色
gradient = ctx.createLinearGradient(0, 0, 0, 300);
gradient.addColorStop(0, 'green');
gradient.addColorStop(1, 'rgba(67,203,36,1)');
}
--绘制图表的轴和标记 (接着上一步的代码写在 goBarChart方法中 )

drawLineLabelMarkers(); // 绘制图表轴、标签和标记
// 绘制图表轴、标签和标记
function drawLineLabelMarkers(){
ctx.translate(0.5,0.5); // 当只绘制1像素的线的时候,坐标点需要偏移,这样才能画出1像素实线
ctx.font = "12px Arial";
ctx.lineWidth = 1;
ctx.fillStyle = "#000";
ctx.strokeStyle = "#000";
// y轴
drawLine(originX, originY, originX, cMargin);
// x轴
drawLine(originX, originY, originX+cWidth, originY);
// 绘制标记
drawMarkers();
ctx.translate(-0.5,-0.5); // 还原位置
}
// 画线的方法
function drawLine(x, y, X, Y){
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(X, Y);
ctx.stroke();
ctx.closePath();
}
// 绘制标记
function drawMarkers(){
ctx.strokeStyle = "#E0E0E0";
// 绘制 y
var oneVal = parseInt(maxValue/totalYNomber);
ctx.textAlign = "right";
for(var i=0; i<=totalYNomber; i++){
var markerVal = i*oneVal;
var xMarker = originX-5;
var yMarker = parseInt( cHeight*(1-markerVal/maxValue) ) + cMargin;
//console.log(xMarker, yMarker+3,markerVal/maxValue,originY);
ctx.fillText(markerVal, xMarker, yMarker+3, cSpace); // 文字
if(i>0){
drawLine(originX, yMarker, originX+cWidth, yMarker);
}
}
// 绘制 x
ctx.textAlign = "center";
for(var i=0; i<tobalBars; i++){
var markerVal = dataArr[i][0];
var xMarker = parseInt( originX+cWidth*(i/tobalBars)+bMargin+bWidth/2 );
var yMarker = originY+15;
ctx.fillText(markerVal, xMarker, yMarker, cSpace); // 文字
}
// 绘制标题 y
ctx.save();
ctx.rotate(-Math.PI/2);
ctx.fillText("产 量", -canvas.height/2, cSpace-10);
ctx.restore();
// 绘制标题 x
ctx.fillText("年份", originX+cWidth/2, originY+cSpace/2+10);
};
-- 绘制柱状图(接着上一步的代码写在 goBarChart方法中 )
drawBarAnimate(); // 绘制柱状图的动画
//绘制柱形图
function drawBarAnimate(mouseMove){
for(var i=0; i<tobalBars; i++){
var oneVal = parseInt(maxValue/totalYNomber);
var barVal = dataArr[i][1];
var barH = parseInt( cHeight*barVal/maxValue * ctr/numctr );
var y = originY - barH;
var x = originX + (bWidth+bMargin)*i + bMargin;
drawRect( x, y, bWidth, barH, mouseMove ); //高度减一避免盖住x轴
ctx.fillText(parseInt(barVal*ctr/numctr), x+15, y-8); // 文字
}
if(ctr<numctr){
ctr++;
setTimeout(function(){
ctx.clearRect(0,0,canvas.width, canvas.height);
drawLineLabelMarkers();
drawBarAnimate();
}, speed);
}
}
//绘制方块
function drawRect( x, y, X, Y, mouseMove ){ ctx.beginPath();
ctx.rect( x, y, X, Y );
if(mouseMove && ctx.isPointInPath(mousePosition.x, mousePosition.y)){ //如果是鼠标移动的到柱状图上,重新绘制图表
ctx.fillStyle = "green";
}else{
ctx.fillStyle = gradient;
ctx.strokeStyle = gradient;
}
ctx.fill();
ctx.closePath(); }
--检测鼠标移动并显示当前项(接着上一步的代码写在 goBarChart方法中 )
注:这里鼠标移动的检测在有文字缩放显示的高清屏幕上会有偏差不准确的情况,而且在高清屏幕中canvas中的文字会略显模糊,以后的章节中会说明如何处理这个问题,大家可以先不管这个问题。(github上的 柱状图-高清.html也已经解决了这个问题,你可以点击上面的下载链接去查看源码)
//检测鼠标移动
var mouseTimer = null;
canvas.addEventListener("mousemove",function(e){
e = e || window.event;
if( e.offsetX || e.offsetX==0 ){
mousePosition.x = e.offsetX;
mousePosition.y = e.offsetY;
}else if( e.layerX || e.layerX==0 ){
mousePosition.x = e.layerX;
mousePosition.y = e.layerY;
} clearTimeout(mouseTimer);
mouseTimer = setTimeout(function(){
ctx.clearRect(0,0,canvas.width, canvas.height);
drawLineLabelMarkers();
drawBarAnimate(true);
},10);
});
--当点击canvas的时候重新刷新图表(接着上一步的代码写在 goBarChart方法中 )
//点击刷新图表
canvas.onclick = function(){
initChart(); // 图表初始化
drawLineLabelMarkers(); // 绘制图表轴、标签和标记
drawBarAnimate(); // 绘制折线图的动画
};

这样我们整个代码就编写完成了,为了代码更便于阅读,我们可以将所有方法放到后面,把调用方法的代码放到前面,经过调整的全部代码如下
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<canvas id="barChart" height="400" width="600" style="margin:50px"> 你的浏览器不支持HTML5 canvas </canvas> <script type="text/javascript">
function goBarChart(dataArr){
// 声明所需变量
var canvas,ctx;
// 图表属性
var cWidth, cHeight, cMargin, cSpace;
var originX, originY;
// 柱状图属性
var bMargin, tobalBars, bWidth, maxValue;
var totalYNomber;
var gradient; // 运动相关变量
var ctr, numctr, speed;
//鼠标移动
var mousePosition = {}; // 获得canvas上下文
canvas = document.getElementById("barChart");
if(canvas && canvas.getContext){
ctx = canvas.getContext("2d");
}
initChart(); // 图表初始化
drawLineLabelMarkers(); // 绘制图表轴、标签和标记
drawBarAnimate(); // 绘制柱状图的动画
//检测鼠标移动
var mouseTimer = null;
canvas.addEventListener("mousemove",function(e){
e = e || window.event;
if( e.layerX || e.layerX==0 ){
mousePosition.x = e.layerX;
mousePosition.y = e.layerY;
}else if( e.offsetX || e.offsetX==0 ){
mousePosition.x = e.offsetX;
mousePosition.y = e.offsetY;
} clearTimeout(mouseTimer);
mouseTimer = setTimeout(function(){
ctx.clearRect(0,0,canvas.width, canvas.height);
drawLineLabelMarkers();
drawBarAnimate(true);
},10);
}); //点击刷新图表
canvas.onclick = function(){
initChart(); // 图表初始化
drawLineLabelMarkers(); // 绘制图表轴、标签和标记
drawBarAnimate(); // 绘制折线图的动画
}; // 图表初始化
function initChart(){
// 图表信息
cMargin = 30;
cSpace = 60;
cHeight = canvas.height - cMargin*2 - cSpace;
cWidth = canvas.width - cMargin*2 - cSpace;
originX = cMargin + cSpace;
originY = cMargin + cHeight; // 柱状图信息
bMargin = 15;
tobalBars = dataArr.length;
bWidth = parseInt( cWidth/tobalBars - bMargin );
maxValue = 0;
for(var i=0; i<dataArr.length; i++){
var barVal = parseInt( dataArr[i][1] );
if( barVal > maxValue ){
maxValue = barVal;
}
}
maxValue += 50;
totalYNomber = 10;
// 运动相关
ctr = 1;
numctr = 100;
speed = 10; //柱状图渐变色
gradient = ctx.createLinearGradient(0, 0, 0, 300);
gradient.addColorStop(0, 'green');
gradient.addColorStop(1, 'rgba(67,203,36,1)'); } // 绘制图表轴、标签和标记
function drawLineLabelMarkers(){
ctx.translate(0.5,0.5); // 当只绘制1像素的线的时候,坐标点需要偏移,这样才能画出1像素实线
ctx.font = "12px Arial";
ctx.lineWidth = 1;
ctx.fillStyle = "#000";
ctx.strokeStyle = "#000";
// y轴
drawLine(originX, originY, originX, cMargin);
// x轴
drawLine(originX, originY, originX+cWidth, originY); // 绘制标记
drawMarkers();
ctx.translate(-0.5,-0.5); // 还原位置
} // 画线的方法
function drawLine(x, y, X, Y){
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(X, Y);
ctx.stroke();
ctx.closePath();
} // 绘制标记
function drawMarkers(){
ctx.strokeStyle = "#E0E0E0";
// 绘制 y
var oneVal = parseInt(maxValue/totalYNomber);
ctx.textAlign = "right";
for(var i=0; i<=totalYNomber; i++){
var markerVal = i*oneVal;
var xMarker = originX-5;
var yMarker = parseInt( cHeight*(1-markerVal/maxValue) ) + cMargin;
//console.log(xMarker, yMarker+3,markerVal/maxValue,originY);
ctx.fillText(markerVal, xMarker, yMarker+3, cSpace); // 文字
if(i>0){
drawLine(originX, yMarker, originX+cWidth, yMarker);
}
}
// 绘制 x
ctx.textAlign = "center";
for(var i=0; i<tobalBars; i++){
var markerVal = dataArr[i][0];
var xMarker = parseInt( originX+cWidth*(i/tobalBars)+bMargin+bWidth/2 );
var yMarker = originY+15;
ctx.fillText(markerVal, xMarker, yMarker, cSpace); // 文字
}
// 绘制标题 y
ctx.save();
ctx.rotate(-Math.PI/2);
ctx.fillText("产 量", -canvas.height/2, cSpace-10);
ctx.restore();
// 绘制标题 x
ctx.fillText("年份", originX+cWidth/2, originY+cSpace/2+10);
}; //绘制柱形图
function drawBarAnimate(mouseMove){
for(var i=0; i<tobalBars; i++){
var oneVal = parseInt(maxValue/totalYNomber);
var barVal = dataArr[i][1];
var barH = parseInt( cHeight*barVal/maxValue * ctr/numctr );
var y = originY - barH;
var x = originX + (bWidth+bMargin)*i + bMargin;
drawRect( x, y, bWidth, barH, mouseMove ); //高度减一避免盖住x轴
ctx.fillText(parseInt(barVal*ctr/numctr), x+15, y-8); // 文字
}
if(ctr<numctr){
ctr++;
setTimeout(function(){
ctx.clearRect(0,0,canvas.width, canvas.height);
drawLineLabelMarkers();
drawBarAnimate();
}, speed);
}
} //绘制方块
function drawRect( x, y, X, Y, mouseMove ){ ctx.beginPath();
ctx.rect( x, y, X, Y );
if(mouseMove && ctx.isPointInPath(mousePosition.x, mousePosition.y)){ //如果是鼠标移动的到柱状图上,重新绘制图表
ctx.fillStyle = "green";
}else{
ctx.fillStyle = gradient;
ctx.strokeStyle = gradient;
}
ctx.fill();
ctx.closePath(); } } goBarChart(
[[2007, 750], [2008, 425], [2009, 960], [2010, 700], [2011, 800], [2012, 975], [2013, 375], [2014, 775]]
) </script>
</body>
</html>
好了,今天就讲到这里,希望大家把代码都自己敲一遍。
关注公众号,博客更新即可收到推送

canvas图表详解系列(1):柱状图的更多相关文章
- canvas图表详解系列(2):折线图
本章建议学习时间4小时 学习方式:详细阅读,并手动实现相关代码(如果没有canvas基础,需要先学习前面的canvas基础笔记) 学习目标:此教程将教会大家如何使用canvas绘制各种图表,详细分解步 ...
- canvas图表详解系列(4):动态散点图
本章建议学习时间4小时 学习方式:详细阅读,并手动实现相关代码(如果没有canvas基础,需要先学习前面的canvas基础笔记) 学习目标:此教程将教会大家如何使用canvas绘制各种图表,详细分解步 ...
- canvas图表详解系列(5):雷达(面积)图
雷达(面积)图 本章建议学习时间4小时 学习方式:详细阅读,并手动实现相关代码(如果没有canvas基础,需要先学习前面的canvas基础笔记) 学习目标:此教程将教会大家如何使用canvas绘制各种 ...
- canvas图表详解系列(3):动态饼状图(原生Js仿echarts饼状图)
本章建议学习时间4小时 学习方式:详细阅读,并手动实现相关代码(如果没有canvas基础,需要先学习前面的canvas基础笔记) 学习目标:此教程将教会大家如何使用canvas绘制各种图表,详细分解步 ...
- 【转】Android Canvas绘图详解(图文)
转自:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2012/1212/703.html Android Canvas绘图详解(图文) 泡 ...
- Python绘制六种可视化图表详解,三维图最炫酷!你觉得呢?
Python绘制六种可视化图表详解,三维图最炫酷!你觉得呢? 可视化图表,有相当多种,但常见的也就下面几种,其他比较复杂一点,大都也是基于如下几种进行组合,变换出来的.对于初学者来说,很容易被这官网上 ...
- JDBC详解系列(二)之加载驱动
---[来自我的CSDN博客](http://blog.csdn.net/weixin_37139197/article/details/78838091)--- 在JDBC详解系列(一)之流程中 ...
- JDBC详解系列(三)之建立连接(DriverManager.getConnection)
在JDBC详解系列(一)之流程中,我将数据库的连接分解成了六个步骤. JDBC流程: 第一步:加载Driver类,注册数据库驱动: 第二步:通过DriverManager,使用url,用户名和密码 ...
- Android高效率编码-第三方SDK详解系列(三)——JPush推送牵扯出来的江湖恩怨,XMPP实现推送,自定义客户端推送
Android高效率编码-第三方SDK详解系列(三)--JPush推送牵扯出来的江湖恩怨,XMPP实现推送,自定义客户端推送 很久没有更新第三方SDK这个系列了,所以更新一下这几天工作中使用到的推送, ...
随机推荐
- poj 3694双联通缩点+LCA
题意:给你一个无向连通图,每次加一条边后,问图中桥的数目. 思路:先将图进行双联通缩点,则缩点后图的边就是桥,然后dfs记录节点深度,给出(u,v)使其节点深度先降到同一等级,然后同时降等级直到汇合到 ...
- Java学习记录:降低耦合度
耦合度定义 耦合度(Coupling)是对模块间关联程度的度量.耦合的强弱取决与模块间接口的复杂性.调用模块的方式以及通过界面传送数据的多少. 模块间的耦合度是指模块之间的依赖关系,包括控制关系.调用 ...
- [2017BUAA软工助教]评论汇总
一 邹欣 周筠 飞龙 二 学校 课程 教师 助教1 助教2 助教3 福州 软件工程1715K 柯逍 谢涛 软件工程1715Z 张栋 刘乾 汪培侨 软件工程1715W 汪璟玢 曾逸群 卞倩虹 李娟 集美 ...
- 201521123007《Java程序设计》第7周学习总结
1. 本周学习总结 2. 书面作业 1. ArrayList代码分析 1.1 解释ArrayList的contains源代码 以下是ArrayList的contains源代码: public bool ...
- 201521123044 《Java程序设计》第7周学习总结
1. 本章学习总结 2. 书面作业 1.ArrayList代码分析 1.1 解释ArrayList的contains源代码 源代码: //contains public boolean contain ...
- 201521123074 《Java程序设计》第5周学习总结
1.本周学习总结 1.1 尝试使用思维导图总结有关多态与接口的知识点. 1.2 可选:使用常规方法总结其他上课内容. 接口定义了解:接口(interface)就是方法声明和常量值的集合. 几种接口讲解 ...
- 201521123010 《Java程序设计》第2周学习总结
1. 本周学习总结 这周学习了在JAVA里各种数据类型的使用.各种运算符的使用.表达是的使用,还初步学习了枚举的用法,也掌握了一些枚举和switch语句结合的用法,还了解了一些字符串类.在实验课上也学 ...
- Java课程设计 学生基本信息管理个人博客
学生基本信息管理系统个人博客 团队课程设计链接 http://www.cnblogs.com/ll321/p/7067598.html 个人负责模块 负责部分界面设计,处理代码: 处理部分数据库数据. ...
- 201521123031 《Java程序设计》第13周学习总结
1. 本周学习总结 以你喜欢的方式(思维导图.OneNote或其他)归纳总结多网络相关内容. 2. 书面作业 1. 网络基础 1.1 比较ping www.baidu.com与ping cec.jmu ...
- mount挂载和交换分区swap
目录 mount挂载 挂载方法 选项 查看设备 卸载命令 文件挂载配置文件fstab 交换文件与分区 swap优先级 三个工具free,df,du 扩展 移动介质 使用光盘 挂载USB设备 mount ...