canvas实现温度热力图、温度分布图。现在市场上的热力图都是根据同一点的数量来表示当前点的热度。随着物联网温度传感器的使用,越来越多的业务开始监控一个平面或者一个区域的温度,然而这种监控的可视化是现在的echarts也没有提供很好的表现形式。我就自己研究了一下,简单实现一下,温度分布图。
下面是效果图
canvas实现
第一步 获取canvas的dom
在项目合适的位置加入canvas画布,获取画布的dom,本demo目前为js实现,vue中请自行解决,后续会更新。
<canvas id="canvas" width="600" height="600px"></canvas>
var canvas= document.getElementById("canvas");
var context = canvas.getContext("2d");
第二步 准备数据
这里我准备了30个点5*6的矩形数据,图像的放大比例,也就是每个点的间隔,通过自己canvas画布的大小按比例进行调整。
var xData = [{
"x": 1,
"y": 1,
"measValue": 15.57
},{
"x": 2,
"y": 1,
"measValue": 14.3
}, {
"x": 3,
"y": 1,
"measValue": 23.67
}, {
"x": 4,
"y": 1,
"measValue": 28.5
}, {
"x": 5,
"y": 1,
"measValue": 14.54
}, {
"x": 5,
"y": 2,
"measValue": 12.44
}, {
"x": 4,
"y": 2,
"measValue": 23.04
}, {
"x": 3,
"y": 2,
"measValue": 20.11
}, {
"x": 2,
"y": 2,
"measValue": 15.08
}, {
"x": 1,
"y": 2,
"measValue": 19.71
}, {
"x": 1,
"y": 3,
"measValue": 23.47
}, {
"x": 2,
"y": 3,
"measValue": 15.51
}, {
"x": 3,
"y": 3,
"measValue": 13.98
}, {
"x": 4,
"y": 3,
"measValue": 22.78
}, {
"x": 5,
"y": 3,
"measValue": 22.63
}, {
"x": 5,
"y": 4,
"measValue": 15.41
}, {
"x": 4,
"y": 4,
"measValue": 14.22
}, {
"x": 3,
"y": 4,
"measValue": 23.2
}, {
"x": 2,
"y": 4,
"measValue": 20.51
}, {
"x": 1,
"y": 4,
"measValue": 13.76
}, {
"x": 1,
"y": 5,
"measValue": 20.04
}, {
"x": 2,
"y": 5,
"measValue": 22.95
}, {
"x": 3,
"y": 5,
"measValue": 12.73
}, {
"x": 4,
"y": 5,
"measValue": 15.19
}, {
"x": 5,
"y": 5,
"measValue": 22.51
}, {
"x": 5,
"y": 6,
"measValue": 22.28
}, {
"x": 4,
"y": 6,
"measValue": 13.22
}, {
"x": 3,
"y": 6,
"measValue": 14.44
}, {
"x": 2,
"y": 6,
"measValue": 23.06
}, {
"x": 1,
"y": 6,
"measValue": 19.13
}];
var circles = [];//准备一个数组后续会用到
for (var i = 0; i < xData.length; i++) {
//这里将横纵坐标进行放大,可以根据自己需求进行调节
var x = xData[i].x*70+80;
var y = (height - xData[i].y*70)-70;//height为canvas高度
var value = xData[i].measValue;
circles.push({
x : x ,
y : y ,
radius : 20,
value : value,
text : xData[i].x + '行' +xData[i].y +'列,温度:'+value
});
}
第三步 开始画点
以坐标点画圆,也需要根据上一步中的间隔,合理调整圆的半径大小,其中max和min是最高温和最低温,此demo中以30°为最高温,可以根据自己项目不同来调整温度的最大值。注意:温度最大值是代表的颜色最大值,而不是你所有环境温度的最大值。
circles.forEach(point => {
let {x, y, value} = point;
let radius = 125;//圆的大小
let max = 30;//最高温度,我这里设置30°
let min = 0;//最低温度,我这里设置30°
let stopColor;
if (value>15){
stopColor = (value - min) / (max - min);
}else{
stopColor = (value - min) / (max - min);
}
context.beginPath();
context.arc(x, y, radius, 0, 2 * Math.PI);
context.closePath();
// 创建渐变色: r,g,b取值比较自由,我们只关注alpha的数值
let radialGradient = context.createRadialGradient(x, y, 0, x, y, radius);
radialGradient.addColorStop(0.0, "rgba(0,0,0,"+stopColor+")");
radialGradient.addColorStop(stopColor, "rgba(0,0,0,0)");
context.fillStyle = radialGradient;
context.fill();
});
效果图如下
第四步 通过绘制渐变色取色 重新渲染
通过定义的渐变色区间进行取色,渐变色设置在下面的tempMap.js中可以修改,0-1代表最低到最高的颜色,根据第三步中的最高温以及百分比,可以算出临界点的温度颜色。比如30度的0.4就是12°时的温度颜色
var tempMap = new TempMap();//在下面会给出
let imageData = context.getImageData(0, 0, width, height);
let data2 = imageData.data;
for (var i = 3; i < data2.length; i+=4) {
let alpha = data2[i];
let color = tempMap.colorPicker(alpha);
data2[i - 3] = color[0];
data2[i - 2] = color[1];
data2[i - 1] = color[2];
}
context.putImageData(imageData, 0, 0);
完成啦
渐变色js实现
tempMap.js
以下是完整版的js代码
(function () {
const defaultColorStops = {
0: "#0000ff",
0.4: "#0000ff",
0.6: "#0f0",
0.8: "#ff0",
1: "#f00",
};
const width = 20, height = 256;
function TempMap(opts) {
Object.assign(this, opts);
this.init();
}
TempMap.prototype.init = function() {
let colorStops = this.colorStops || defaultColorStops;
// 创建canvas
let canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
let ctx = canvas.getContext("2d");
// 创建线性渐变色
let linearGradient = ctx.createLinearGradient(0, 0, 0, height);
for (const key in colorStops) {
linearGradient.addColorStop(key, colorStops[key]);
}
// 绘制渐变色条
ctx.fillStyle = linearGradient;
ctx.fillRect(0, 0, width, height);
// 读取像素数据
this.imageData = ctx.getImageData(0, 0, 1, height).data;
this.canvas = canvas;
};
/**
* 取色器
* @param {Number} position 像素位置
* @return {Array.<Number>} [r, g, b]
*/
TempMap.prototype.colorPicker = function(position) {
return this.imageData.slice(position * 4, position * 4 + 3);
};
// amd
if (typeof define !== 'undefined' && define.amd) define(function () {
return TempMap;
});
// common js
if (typeof exports !== 'undefined') exports.TempMap = TempMap;
// browser
else if (typeof window !== 'undefined') window.TempMap = TempMap;
// nodejs
if (typeof module !== 'undefined') {
module.exports = TempMap;
}
}());