本章建议学习时间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):柱状图的更多相关文章

  1. canvas图表详解系列(2):折线图

    本章建议学习时间4小时 学习方式:详细阅读,并手动实现相关代码(如果没有canvas基础,需要先学习前面的canvas基础笔记) 学习目标:此教程将教会大家如何使用canvas绘制各种图表,详细分解步 ...

  2. canvas图表详解系列(4):动态散点图

    本章建议学习时间4小时 学习方式:详细阅读,并手动实现相关代码(如果没有canvas基础,需要先学习前面的canvas基础笔记) 学习目标:此教程将教会大家如何使用canvas绘制各种图表,详细分解步 ...

  3. canvas图表详解系列(5):雷达(面积)图

    雷达(面积)图 本章建议学习时间4小时 学习方式:详细阅读,并手动实现相关代码(如果没有canvas基础,需要先学习前面的canvas基础笔记) 学习目标:此教程将教会大家如何使用canvas绘制各种 ...

  4. canvas图表详解系列(3):动态饼状图(原生Js仿echarts饼状图)

    本章建议学习时间4小时 学习方式:详细阅读,并手动实现相关代码(如果没有canvas基础,需要先学习前面的canvas基础笔记) 学习目标:此教程将教会大家如何使用canvas绘制各种图表,详细分解步 ...

  5. 【转】Android Canvas绘图详解(图文)

    转自:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2012/1212/703.html Android Canvas绘图详解(图文) 泡 ...

  6. Python绘制六种可视化图表详解,三维图最炫酷!你觉得呢?

    Python绘制六种可视化图表详解,三维图最炫酷!你觉得呢? 可视化图表,有相当多种,但常见的也就下面几种,其他比较复杂一点,大都也是基于如下几种进行组合,变换出来的.对于初学者来说,很容易被这官网上 ...

  7. JDBC详解系列(二)之加载驱动

    ---[来自我的CSDN博客](http://blog.csdn.net/weixin_37139197/article/details/78838091)---   在JDBC详解系列(一)之流程中 ...

  8. JDBC详解系列(三)之建立连接(DriverManager.getConnection)

      在JDBC详解系列(一)之流程中,我将数据库的连接分解成了六个步骤. JDBC流程: 第一步:加载Driver类,注册数据库驱动: 第二步:通过DriverManager,使用url,用户名和密码 ...

  9. Android高效率编码-第三方SDK详解系列(三)——JPush推送牵扯出来的江湖恩怨,XMPP实现推送,自定义客户端推送

    Android高效率编码-第三方SDK详解系列(三)--JPush推送牵扯出来的江湖恩怨,XMPP实现推送,自定义客户端推送 很久没有更新第三方SDK这个系列了,所以更新一下这几天工作中使用到的推送, ...

随机推荐

  1. Python闭包及其作用域

    Python闭包及其作用域 关于Python作用域的知识在python作用域有相应的笔记,这个笔记是关于Python闭包及其作用域的详细的笔记 如果在一个内部函数里,对一个外部作用域(但不是全局作用域 ...

  2. spring cloud认识

    Spring Cloud是一个基于Spring Boot实现的云应用开发工具,它为基于JVM的云应用开发中的配置管理.服务发现.断路器.智能路由.微代理.控制总线.全局锁.决策竞选.分布式会话和集群状 ...

  3. linux每日一练:Enable multithreading to use std::thread: Operation not permitted问题解决

    linux每日一练:Enable multithreading to use std::thread: Operation not permitted问题解决 在linux在需要使用c++11时会遇到 ...

  4. 结对编程1-基于GUI的四则运算生成器

    201421123016郑怀勇     201421123017康建灿 程序代码 / 康建灿 一.需求分析 记录用户的对错总数. 程序退出再启动的时候,能把以前的对错数量保存并在此基础上增量计算. 有 ...

  5. 【Beta】Daily Scrum Meeting——Day5

    站立式会议照片 1.本次会议为第五次Meeting会议: 2.本次会议在早上9:35,在陆大2楼机房召开,本次会议为25分钟讨论今天要完成的任务以及接下来的任务安排. 燃尽图 每个人的工作分配 成 员 ...

  6. 扫雷游戏制作过程(C#描述):第四节、菜单操作

    前言 这里给出教程原文地址. 该项目已经放在github上托管. 菜单操作 我们现在的程序单击菜单的时候不会有任何反应,这一节我们主要介绍菜单的相关代码,使得菜单能够正常使用. 现在我们希望在对应级别 ...

  7. 201521123040《Java程序设计》第5周学习总结

    1. 本周学习总结 1.1 尝试使用思维导图总结有关多态与接口的知识点. 2. 书面作业 1.代码阅读:Child压缩包内源代码 1.1 com.parent包中Child.java文件能否编译通过? ...

  8. 201521123112《Java程序设计》第2周学习总结

    1.本周学习总结 本周在课堂面授课粗略讲了<Java学习笔记>中的第三章,其内容大部分都与上学期学习的数据结构差不多,所以只是粗略的复习了一下就带过,然后通过将PTA上的实验便于我们本周的 ...

  9. Java中如何引入结对编程

    引自微信: 很多同学说: 我程序写得好,ACM比赛能得分, 就好了,软件工程讲的那些有用么? 有些学校的 <软件工程>课,由于要求太简单,反而不能说明软件工程的价值. 其实好办, 让学生结 ...

  10. [python学习笔记] 数据类型与语法

    数据类型 数值型 int 整形 没有long类型,可以代表任意大小的整数. type(1) -> int float 浮点数 也没有double类型 type(1.2) -> float ...