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这个系列了,所以更新一下这几天工作中使用到的推送, ...
随机推荐
- linux下使用gcc编译运行C程序
gcc(GNU Compiler Collection)是Linux下最常用的C语言编译器,是GNU项目中符合ANSI C标准的编译系统,能够编译用C.C++和Object C等语言编写的程序. 在 ...
- POJ3228 并查集或二分最大流枚举答案
忘记写题意了.这题题意:给出每个地点的金矿与金库的数量,再给出边的长度.求取最大可通过边长的最小权值使每个金矿都能运输到金库里. 这题和之前做的两道二分枚举最大流答案的问法很相识,但是这里用最大流速度 ...
- NWERC2016-Problem A(Arranging Hat)
Arranging Hat is a cushy job indeed; high impact work, absolute authority, and 364 days of holiday e ...
- Apache配置虚拟域名
在作php本地调试的时候,一般都要打上localhost/,如果你的项目层级关系比较多,那你的url地址就会很长. 那我们能不能用一个简短的域名去替代那些一长串无用的字符呢? 那可能有人会问如果我没有 ...
- 【2017集美大学1412软工实践_助教博客】团队作业5——测试与发布(Alpha版本)
第五次团队作业成绩公布 题目 团队作业5: http://www.cnblogs.com/happyzm/p/6788792.html 团队成绩 成绩公示如下: 检查项 测试报告 Alpha版本发布说 ...
- 201521123107 《Java程序设计》第9周学习总结
第9周作业-异常 1.本周学习总结 2.书面作业 本次PTA作业题集异常 1.常用异常 题目5-1 1.1 截图你的提交结果(出现学号) 1.2 自己以前编写的代码中经常出现什么异常.需要捕获吗(为什 ...
- 软件工程资料 - UCSD 怎么教软件工程
2012年,在上软件工程课的过程中,收集到这个博客,作为学习资料. 原网站 (http://www.arc-trooper.com)已经不存在,这是一个备份. ----------------- 以下 ...
- 201521123042《Java程序设计》 第7周学习总结
1. 本周学习总结 网上看了很多资料,发现这一张图总结的还不错就引用过来了.但是最上面的Map和Collection之间的关系应该是依赖,不是Produces. ①概述:Java集合框架主要包括两种类 ...
- 201521123055 《Java程序设计》第9周学习总结
1. 本章学习总结 2. 书面作业 Q.1常用异常 题目5-1 1.1 截图你的提交结果(出现学号) 1.2 自己以前编写的代码中经常出现什么异常.需要捕获吗(为什么)?应如何避免? 1.3 什么样的 ...
- Hibernate的Configuration对象的configure()方法
Configuration configuration=new Configuration(); configuration.configure(); 在Hibernate底层实现configure( ...