使用到了Colormap,可以去npm上下载一个,查看下使用文档,来用数据对应颜色,在画布上显示
其中碰到了一些坑,canvas的画布大小属性和style设置的不是同一个东西,这个和svg的一样
<script src="./Colormap.js"></script>
<button>+1 绘制</button><br /><br />
<div class="myChart">
<div class="info"></div>
<canvas id="myCanvas" style="border: 1px solid #999;"></canvas>
</div>
// 数据模拟
var xData = [];
for (let i = 0; i <= 2260; i++) {
xData.push(i)
};
// canvas四周间隔
var gridGap = 40;
// canvas : 0-150数值,对应的颜色值
// 数据值 => 颜色映射
var maxValue = 150;
var dataToColor = createColormap({
colormap: "jet",
nshades: maxValue,
format: "hex",
alpha: 1,
});
// canvas
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
// canvas: 设置真实高度, 行内宽高是物理性宽高
var canvasWidth = canvas.width = 600;
var canvasHeight = canvas.height = 200;
canvasWidth -= gridGap * 2;
canvasHeight -= gridGap;
// canvas: 文字设置
ctx.font = "12px '微软雅黑'"; //设置字体
ctx.textAlign = "center";
ctx.textBaseline = 'hanging'; //在绘制文本时使用的当前文本基线
// 图例
var gradient = ctx.createLinearGradient(0, 0, 0, canvasHeight);
// 图例 : 线性渐变,根据maxValue数值渲染
for (let i = 0; i < maxValue; i++) {
gradient.addColorStop(i / maxValue, dataToColor[i]);
};
ctx.fillStyle = gradient;
ctx.fillRect(25, 0, 10, canvasHeight);
// 图例: 数字说明
ctx.fillStyle = "#666";
ctx.fillText(0, 10, canvasHeight - 10); //设置文本内容
ctx.fillText(maxValue / 2, 10, (canvasHeight - 10) / 2); //设置文本内容
ctx.fillText(maxValue, 10, 0); //设置文本内容
// 时间轴
var testTime = ['7:00', '7:20', '7:40', '8:00', '8:20', '8:40', '9:00', '9:20', '9:40', '10:00'];
var timeArr = [];
function time(data) {
// 清除之前绘制的
ctx.clearRect(canvasWidth + gridGap, 0, gridGap, canvasHeight);
// 重新绘制
data.forEach((val, index) => {
ctx.fillStyle = "#666";
ctx.fillText(val, canvasWidth + gridGap * 1.5, (canvasHeight / 10) * index); //设置文本内容
});
};
// 数据最多10条
// 绘制 X 轴
var xNum = 10; // 10个分割点
var maxUnit = Math.ceil((Math.max(...xData) / 10)); // 分割单位(显示的文字)
ctx.translate(0.5, 0.5); // 防止1像素模糊
// X轴: 线
ctx.beginPath();
ctx.moveTo(gridGap, canvasHeight);
ctx.lineTo(canvasWidth + gridGap, canvasHeight);
ctx.strokeStyle = "#aaa"
ctx.stroke();
ctx.closePath();
// X轴 :分割线 + 文字
var x_gap = canvasWidth / 10;
for (let i = 0; i <= 10; i++) {
var posX = i * x_gap + gridGap;
ctx.beginPath();
ctx.moveTo(posX, canvasHeight);
ctx.lineTo(posX, canvasHeight + 10);
ctx.fillStyle = "#666";
ctx.fillText(maxUnit * i, posX, canvasHeight + 10); //设置文本内容
ctx.strokeStyle = "#aaa";
ctx.stroke();
};
// 一个单元色块的大小
var unitWidth = 150;
var unitHeight = 30;
// 绘制单元
function drawUnit(posX, posY, w, h, color) {
ctx.beginPath();
ctx.fillStyle = color;
ctx.fillRect(posX, posY, w, h);
ctx.closePath();
};
var tempData = null; // 缓存之前canvasData
document.querySelector('button').onclick = function () {
// 绘制时间轴
timeArr.unshift(Math.ceil(Math.random() * 100));
timeArr.length = timeArr.length > 10 ? 10 : timeArr.length;
time(timeArr);
// 填充之前已绘制数据,并且Y轴位置下降一个单位高度
tempData && ctx.putImageData(tempData, 0 + gridGap, unitHeight);
// 横向填充次数, 并且数据绘制,永远在第一行,以前的数据下沉
for (let i = 0; i < Math.ceil(canvasWidth / unitWidth); i++) {
//当前单元坐标+单元宽度,超出有效画布时,裁剪绘制的单元
var x = i * unitWidth;
if (x + unitWidth > canvasWidth) {
drawUnit(x + gridGap, 0, canvasWidth - x, unitHeight, dataToColor[~~(Math.random() * 150)]);
} else {
drawUnit(x + gridGap, 0, unitWidth, unitHeight, dataToColor[~~(Math.random() * 150)]);
}
};
// 获取整个画布高度-单元高度, 再次绘制的时候,填充进去,并且位置下降一个单元块的高度
tempData = ctx.getImageData(0 + gridGap, 0, canvasWidth, canvasHeight - unitHeight);
}
// 鼠标经过,计算出该点在整个画布中的索引位置 => 缓存中的后端数据找出来
// x轴索引 * y轴索引 => 数据位置索引
var infoBox = document.querySelector('.info');
canvas.onmousemove = function (event) {
var posX = event.offsetX;
var posY = event.offsetY;
// X轴+Y轴有效画布区域
var flagX = posX > gridGap && posX < canvasWidth + gridGap;
var flagY = posY < canvasHeight;
if (flagX && flagY) {
// x轴索引
var index_X = Math.ceil((posX - gridGap) / unitWidth);
// y轴索引
var index_Y = Math.ceil(posY / unitHeight);
// 一行的数据个数
var xNum = Math.ceil(canvasWidth / unitWidth);
// 数据索引
var dataNum = (index_Y - 1) * xNum + index_X;
infoBox.innerHTML = dataNum;
var pos = `display:block; left: ${posX}px; top:${posY}px `;
infoBox.style.cssText = pos;
} else {
infoBox.style.display = 'none';
}
};