canvas绘制一个柱状图

253 阅读3分钟

我正在参加「掘金·启航计划」

前言

最近也是在学习canvas,跟着绘制了一个最基本柱状图,canvas是一个基于HTML5的绘图标准,提供了一系列的API可以用来绘制图形,包括直线、矩形、圆形、文本等等。我介绍如何使用canvas绘制简单的柱状图,并优化技巧,使得代码更加高效

image.png

绘制柱状图

在上图的柱状图里有什么?

  • 坐标轴:横轴和纵轴,横轴用来表示数据的分类或者时间,纵轴用来表示数据的大小或者数量
  • 柱子:柱状图的每一个数据点通常用一个矩形或者条形来表示,其高度表示该数据的大小或数量

绘制坐标轴

首先来对画布进行一个初始化,获取canvas元素并获取其2D上下文对象的,对canvas元素的尺寸进行缩放,将canvas元素的CSS宽高设置为它在HTML中设置的宽度和高度,确保canvas在页面中显示的大小不会改变,然后根据设备像素比扩大画布的像素,提高图像的清晰度,防止模糊发虚

<canvas id="canvas" width="600" height="500"></canvas>
<script>
  const canvas = document.getElementById("canvas");
  const ctx = canvas.getContext("2d");
  canvas.style.width = canvas.width + "px";
  canvas.style.height = canvas.height + "px";
  canvas.width = canvas.width * 1.5;
  canvas.height = canvas.height * 1.5;
</script>

然后定义一些相关的属性,在绘制的过程中以备使用

const ht = canvas.clientHeight; //画布高度
const wd = canvas.clientWidth; //画布宽度
const pad = 20; //边距
const bottomPad = 20; //底部边距
const step = 100; //坐标轴刻度间距

首先要把坐标轴的那条直角画出来,设置lineWidth线宽为2,strokeStyle笔画颜色为'lightblue',绘制的起点为定义好的常量pad为20,一直绘制到最底部然后再往右边绘制

// 绘制坐标轴
ctx.beginPath();
ctx.lineWidth = 2;
ctx.strokeStyle = "lightblue";
ctx.moveTo(pad, pad);
ctx.lineTo(pad, ht * 1.5 - bottomPad);
ctx.lineTo(wd * 1.5 - pad, ht * 1.5 - bottomPad);
ctx.stroke();
ctx.closePath();
image.png

然后循环绘制x轴方向刻度,起点x轴固定不变为pad,y轴坐标为画布高度 * 1.5 - bottomPad - i * 坐标轴刻度间距,终点x轴为pad + 10,y轴和起点y轴一致

循环y轴方向刻度也是同理,只不过是反过来了

// 绘制x轴方向刻度
ctx.beginPath();
ctx.lineWidth = 1;
ctx.strokeStyle = "#666";
for (let i = 1; i < Math.floor((ht * 1.5) / step); i++) {
ctx.moveTo(pad, ht * 1.5 - bottomPad - i * step);
ctx.lineTo(pad + 10, ht * 1.5 - bottomPad - i * step);
}
ctx.stroke();
ctx.closePath();

// 绘制y轴方向刻度
ctx.beginPath();
ctx.lineWidth = 1;
ctx.strokeStyle = "#666";
for (let i = 1; i < Math.floor((wd * 1.5) / step); i++) {
ctx.moveTo(pad + i * step, ht * 1.5 - bottomPad);
ctx.lineTo(pad + i * step, ht * 1.5 - bottomPad + 10);
}
ctx.stroke();
ctx.closePath();
image.png

绘制柱子

柱子实际上就是一个矩形,绘制矩形分三种,有描边有填充,无描边有填充,有描边无填充

有描边有填充通过rect方法绘制出一个矩形的路径,但不会实际绘制矩形,还需要通过stroke方法和fill方法来绘制描边和填充

无描边有填充通过fillRect方法绘制一个填充的矩形,有描边无填充通过strokeRect方法绘制一个边框矩形

无论是rect方法,fillRect方法还是strokeRect方法都要传入四个参数:x轴坐标,y轴坐标,宽度,高度

// 绘制矩形填充
ctx.beginPath();
ctx.lineWidth = 5; //线条宽度
ctx.strokeStyle = "orange"; //笔画颜色
ctx.fillStyle = "hotpink"; //填充颜色
ctx.rect(100, 100, 300, 200); //绘制矩形路径
ctx.stroke(); //绘制描边
ctx.fill(); //填充
ctx.closePath();

ctx.beginPath();
ctx.lineWidth = 4;
ctx.strokeStyle = "blue";
ctx.strokeRect(100, 310, 300, 200); //绘制矩形(无填充)
ctx.closePath();

ctx.beginPath();
ctx.fillStyle = "skyblue";
ctx.fillRect(410, 310, 300, 200); //绘制矩形(填充)
ctx.closePath();

image.png

知道如何绘制矩形然后就可以绘制出柱子了,只需要绘制有填充五描边的矩形就行了,于是循环调用fillRect方法绘制

ctx.beginPath();
for (let i = 1; i < Math.floor((wd * 1.5) / step); i++) {
    const height = Math.random() * 500 + 50;
    ctx.fillStyle = "#5470C6";
    ctx.fillRect(i * step, ht * 1.5 - bottomPad, 40, -height);
}
ctx.closePath();
image.png