博客
关于我
【带着canvas去流浪】(1)绘制柱状图
阅读量:412 次
发布时间:2019-03-06

本文共 5877 字,大约阅读时间需要 19 分钟。

示例代码托管在:

博客园地址:

华为云社区地址:

目录

一. 任务说明

使用原生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轴的箭头请自行完成即可。

转载地址:http://rgzuz.baihongyu.com/

你可能感兴趣的文章
80. Remove Duplicates from Sorted Array II
查看>>
83. Remove Duplicates from Sorted List
查看>>
410. Split Array Largest Sum
查看>>
开源项目在闲鱼、b 站上被倒卖?这是什么骚操作?
查看>>
Vue3发布半年我不学,摸鱼爽歪歪,哎~就是玩儿
查看>>
《实战java高并发程序设计》源码整理及读书笔记
查看>>
Java开源博客My-Blog(SpringBoot+Docker)系列文章
查看>>
程序员视角:鹿晗公布恋情是如何把微博搞炸的?
查看>>
Spring+SpringMVC+MyBatis+easyUI整合进阶篇(七)一次线上Mysql数据库崩溃事故的记录
查看>>
【JavaScript】动态原型模式创建对象 ||为何不能用字面量创建原型对象?
查看>>
ClickHouse源码笔记4:FilterBlockInputStream, 探寻where,having的实现
查看>>
Linux应用-线程操作
查看>>
多态体验,和探索爷爷类指针的多态性
查看>>
系统编程-进程间通信-无名管道
查看>>
记2020年初对SimpleGUI源码的阅读成果
查看>>
C语言实现面向对象方法学的GLib、GObject-初体验
查看>>
系统编程-进程-ps命令、进程调度、优先级翻转、进程状态
查看>>
为什么我觉得需要熟悉vim使用,难道仅仅是为了耍酷?
查看>>
一个支持高网络吞吐量、基于机器性能评分的TCP负载均衡器gobalan
查看>>
HDOJ2017_字符串统计
查看>>