【带着canvas去流浪】(1)绘制柱状图
程序员文章站
2022-07-25 23:01:33
示例代码托管在: "http://www.github.com/dashnowords/blogs" 博客园地址: "《大史住在大前端》原创博文目录" 华为云社区地址: "【你要的前端打怪升级指南】" [TOC] 一. 任务说明 使用原生 绘制柱状图。(柱状图截图来自于百度Echarts官方示例库 ......
目录
示例代码托管在: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轴的箭头请自行完成即可。