带着canvas去流浪系列之一:绘制柱状图
【摘要】 学习使用canvasAPI来实现数据可视化。
示例代码托管在:http://www.github.com/dashnowords/blogs
一. 任务说明
使用原生canvasAPI
绘制柱状图。(柱状图截图来自于百度Echarts官方示例库【查看示例链接】)
二. 重点提示
柱状图或许是最容易实现的图表类型了,矩形的部分直接使用fillRect()
来绘制即可,为了将坐标轴标签文字绘制在小分割线中间,需要用measureText()
来测量文本的宽度,然后进行相应的偏移,否则直接绘制的话文字的左边界会和直线相对齐。其他部分都是一些基本API的使用,希望各位小伙伴通过做练习来熟悉这些API的用法。
三. 示例代码
提示:代码中将个别图表参数直接写在了函数里(也就是所谓的“魔鬼数字”),这种做法是不提倡的,因为它违反了开发的基本原则之一“开放封闭原则”。如果你使用过Echarts
图表库就会发现,图表中几乎所有要素都可以通过参数来定制,此处只需要关注canvasAPI
的实现方法即可。
/**
* 获取canvas绘图上下文
* @type {[type]}
*/
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
//绘图配置
let options = {
chartZone:[50,50,1000,700],//标识绘图区域
yAxisLabel:['0','100','200','300','400'],//标示Y轴坐标
yMax:400,//Y轴最大值
xAxisLabel:['Mon','Tue','Wed','Thu','Fri','Sat','Sun'],//X轴坐标
data:[10,50,200,330,390,320,220],//柱状图数据
barStyle:{
width:70,//柱状图宽度
color:'#1abc9c'//柱状图颜色
}
}
/*Echarts使用时,会调用实例方法echartsInstance.setOptions(options)来启动绘图*/
drawBarChart(options);
/**
* 绘制柱状图
*/
function drawBarChart(options) {
drawAxis(options); //绘制坐标轴
drawYLabels(options); //绘制y轴坐标
drawXLabels(options); //绘制x轴坐标
//drawData(options);//绘制柱状图
drawDataGradient(options);//绘制渐变色柱状图
}
/**
* 绘制坐标轴
*/
function drawAxis(options) {
let chartZone = options.chartZone;
context.strokeWidth = 4;
context.strokeStyle = '#353535';
context.moveTo(chartZone[0],chartZone[1]);
context.lineTo(chartZone[0],chartZone[3]); //y轴总高从50到700
context.lineTo(chartZone[2],chartZone[3]); //x轴总长从50到1000
context.stroke();
}
/**
* 绘制y轴坐标
*/
function drawYLabels(options) {
let labels = options.yAxisLabel;
let yLength = (options.chartZone[3] - options.chartZone[1])*0.98;
let gap = yLength / (labels.length - 1);
labels.forEach(function (label, index) {
//绘制坐标文字
let offset = context.measureText(label).width + 20;
context.strokeStyle = '#eaeaea';
context.font = '16px';
context.fillText(label, options.chartZone[0] - offset ,options.chartZone[3] - index * gap);
//绘制小间隔
context.beginPath();
context.strokeStyle = '#353535';
context.moveTo(options.chartZone[0] - 10, options.chartZone[3] - index * gap);
context.lineTo(options.chartZone[0], options.chartZone[3] - index * gap);
context.stroke();
//绘制辅助线
context.beginPath();
context.strokeStyle = '#eaeaea';
context.strokeWidth = 2;
context.moveTo(options.chartZone[0], options.chartZone[3] - index * gap);
context.lineTo(options.chartZone[2], options.chartZone[3] - index * gap);
context.stroke();
});
}
/**
* 绘制x轴坐标
*/
function drawXLabels(options) {
let labels = options.xAxisLabel;
let xLength = (options.chartZone[2] - options.chartZone[0])*0.96;
let gap = xLength / labels.length;
labels.forEach(function (label, index) {
//绘制坐标文字
let offset = context.measureText(label).width;
context.strokeStyle = '#eaeaea';
context.font = '18px';
context.fillText(label, options.chartZone[0] + (index + 1) * gap - offset ,options.chartZone[3] + 20);
//绘制小间隔
context.beginPath();
context.strokeStyle = '#353535';
context.moveTo(options.chartZone[0] + (index + 1) * gap - offset / 2 ,options.chartZone[3]);
context.lineTo(options.chartZone[0] + (index + 1) * gap - offset / 2,options.chartZone[3]+5);
context.stroke();
//存储偏移量
options.offsetXLabel = offset / 2;
});
}
/**
* 绘制数据
*/
function drawData(options) {
let data = options.data;
let xLength = (options.chartZone[2] - options.chartZone[0])*0.96;
let yLength = (options.chartZone[3] - options.chartZone[1])*0.98;
let gap = xLength / options.xAxisLabel.length;
//绘制矩形
data.forEach(function (item, index) {
context.fillStyle = options.barStyle.color || '#1abc9c'; //02BAD4
let x0 = options.chartZone[0] + (index + 1) * gap - options.barStyle.width / 2 - options.offsetXLabel;
let height = item / options.yMax * (options.chartZone[3] - options.chartZone[1])*0.98;
let y0 = options.chartZone[3] - height;
let width = options.barStyle.width;
context.fillRect(x0,y0,width,height);
});
}
/**
* 绘制线性渐变色柱状图
*/
function drawDataGradient(options) {
let data = options.data;
let xLength = (options.chartZone[2] - options.chartZone[0])*0.96;
let yLength = (options.chartZone[3] - options.chartZone[1])*0.98;
let gap = xLength / options.xAxisLabel.length;
//创建渐变色
let fillStyleGradient = context.createLinearGradient(50,50,50,700);
fillStyleGradient.addColorStop(0, options.barStyle.color);
fillStyleGradient.addColorStop(1, 'rgba(1,176,241,0.6)');
//绘制矩形
data.forEach(function (item, index) {
context.fillStyle = fillStyleGradient;
let x0 = options.chartZone[0] + (index + 1) * gap - options.barStyle.width / 2 - options.offsetXLabel;
let height = item / options.yMax * (options.chartZone[3] - options.chartZone[1])*0.98;
let y0 = options.chartZone[3] - height;
let width = options.barStyle.width;
context.fillRect(x0,y0,width,height);
});
}
浏览器中可查看效果:
四. 思考题
如果希望在坐标轴末端加一个箭头,需要怎么做呢?
/*x轴箭头示例*/
//1.options中增加箭头颜色和大小的设置
let options = {
//...
axisArrow:{
size:2,
color:'#DA5961'
}
}
//箭头绘制函数
/**
* x轴绘制箭头
*/
function drawArrow(options) {
let factor = options.axisArrow.size;//获取箭头大小因子
context.save();//保存当前设置的绘图上下文
context.translate(options.chartZone[2], options.chartZone[3]);//移动坐标系原点至x轴末端
context.beginPath();//开始绘制箭头
context.moveTo(0,0);//移动至新原点
context.lineTo(2 * factor,-3 * factor);
context.lineTo(10 * factor,0);
context.lineTo(2 * factor, 3 * factor);
context.lineTo(0,0);
context.globalAlpha = 0.7; //设置填充色透明度
context.fillStyle = options.axisArrow.color;//获取箭头颜色
context.fill();//填充箭头路径
context.restore();//恢复绘图上下文样式设置
}
箭头效果:
y轴的箭头请自行完成即可。
五. 说明
本系列旨在学习使用canvasAPI
来实现数据可视化,暂不考虑图表交互的情况,感兴趣的读者可以尝试采用svg绘图或在canvas上增加蒙版配合DOM元素来实现图表的交互。
来源:华为云社区 作者:大史不说话
带着canvas去流浪系列之一:绘制柱状图的更多相关文章
- 带着canvas去流浪系列之五 绘制K线图
[摘要] 用canvas原生API实现百度Echarts 示例代码托管在:http://www.github.com/dashnowords/blogs 一. 任务说明 使用原生canvasAPI绘制 ...
- 带着canvas去流浪系列之四 绘制散点图
[摘要] 用原生canvasAPI实现百度Echarts图表 示例代码托管在:http://www.github.com/dashnowords/blogs 一. 任务说明 使用原生canvasAPI ...
- 带着canvas去流浪系列之七 绘制水球图
[摘要] 用原生canvasAPI实现百度echarts 示例代码托管在:http://www.github.com/dashnowords/blogs 一. 任务说明 使用原生canvasAPI绘制 ...
- 带着canvas去流浪系列之三 绘制饼图
[摘要] 用canvas原生API绘制Echarts图表 示例代码托管在:http://www.github.com/dashnowords/blogs 一. 任务说明 使用原生canvasAPI绘制 ...
- 带着canvas去流浪系列之六 绘制雷达图
[摘要] 用canvas原生API实现百度Echarts基本图表. 示例代码托管在:http://www.github.com/dashnowords/blogs 一. 任务说明 使用原生canvas ...
- 带着canvas去流浪系列之二 绘制折线图
[摘要] 用canvasAPI实现echarts简易图表 示例代码托管在:http://www.github.com/dashnowords/blogs 一. 任务说明 使用原生canvasAPI绘制 ...
- 带着canvas去流浪系列之八 碰撞
[摘要] canvas动画-碰撞仿真 示例代码托管在:http://www.github.com/dashnowords/blogs 经过前面章节相对枯燥的练习,相信你已经能够上手canvas的原生A ...
- 带着canvas去流浪系列之八 碰撞【华为云技术分享】
[摘要] canvas动画-碰撞仿真 示例代码托管在:http://www.github.com/dashnowords/blogs 经过前面章节相对枯燥的练习,相信你已经能够上手canvas的原生A ...
- 带着canvas去流浪系列之九 粒子动画【华为云技术分享】
版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/devcloud/article/detai ...
随机推荐
- 可爱精灵宝贝:dp
拒绝听搜索.etc水过的.数据太弱了(尽管考场上我凭借数据太水骗了好多分) 我讲的思路和下发的题解一样.(因为我不会所以只能颓它啊) 首先你要相信这题精灵就100个,真的只有100个,这次数据范围没错 ...
- Redis实战--Redis整合SpringBoot示例
echo编辑整理,欢迎转载,转载请声明文章来源.欢迎添加echo微信(微信号:t2421499075)交流学习. 百战不败,依不自称常胜,百败不颓,依能奋力前行.--这才是真正的堪称强大!!! 该文章 ...
- Java自动化测试框架-12 - TestNG之xml文件详解篇 (详细教程)
1.简介 现在这篇,我们来学习TestNG.xml文件,前面我们已经知道,TestNG就是运行这个文件来执行测试用例的.通过本篇,你可以进一步了解到:这个文件是配置测试用例,测试套件.简单来说,利用这 ...
- 「Luogu 1525」关押罪犯
更好的阅读体验 Portal Portal1: Luogu Portal2: LibreOJ Description \(S\)城现有两座监狱,一共关押着\(N\)名罪犯,编号分别为\(1 - N\) ...
- c++中while(cin>>str)和ctrl z的相关问题探讨
对于while (cin>>str)和ctrl z的问题,网上有以下解释: -------------------------------------------------------- ...
- 实现 call、apply、bind
实现 call.apply.bind 在之前一篇文章写了这三个参数的区别,但是其实面试更常考察如何实现.其实所有的原生函数的 polyfill 如何实现,只需要考虑 4 点即可: 基本功能 原型 th ...
- egret编译速度慢解决方法
egret编译速度慢解决方法 直接用增量更新egret run -a 每次改完代码 保存都会自动编译
- 第五天、vim,重定向,用户和组管理
第五天.vim,重定向,用户和组管理 vim vi:Visual editor,文本编辑器 行编辑器:sed 全屏编辑器:vim,vi,nano 其他编辑器gedit,gvim 定义别名让vi等于vi ...
- Swoole跟thinkphp5结合开发WebSocket在线聊天通讯系统
ThinkPHP使用Swoole需要安装 think-swoole Composer包,前提系统已经安装好了Swoole PECL 拓展* tp5的项目根目录下执行composer命令安装think- ...
- win10添加启动项目
Win10启动文件夹一般位于C:\ProgramData\Microsoft\Windows\Start Menu(开始菜单)\Programs(程序)\StartUp(启动)目录,我们主要讲希望添加 ...