热力图长什么样?
(截图来自g2热力图)
什么是热力图?
热力图是数据可视化项目中,比较常用的显示方式。通过颜色变化程度,他可以直观反应出热点分布,区域聚集等数据信息。
“热力图”一词最初是由软件设计师Cormac Kinney于1991年提出并创造的,用来描述一个2D显示实时金融市场信息。最开始的热力图,是矩形色块加上颜色编码。经过多年的演化,习语上的热力图,如今更规范,更被大多数人理解的是这种经过平滑模糊过的热力图谱。
热力图一般是基于离散点、线或面的分析与表达,或者基于连续表面的密度分析得到的。
如何实现热力图?
大致分为三个步骤:
1.为每个数据点设置一个从中心向外灰度渐变的圆。
2.利用灰度可以叠加的原理,计算每个像素点数据交叉叠加得到的灰度值。
3.根据每个像素计算得到的灰度值,在一条彩色色带中进行颜色映射,最后对图像进行着色,得到热力图。
代码实现过程:
1.创建800*800的画布
<canvas id="heatmap" width="800" height="800" style="border:1px solid #000000;"></canvas>
2.声明一个长度为10的list,x、y代表坐标轴,value代表该点的值。比如声音分贝。
var pointsdata = [{
x: 471,
y: 280,
value: 25
}, {
x: 438,
y: 300,
value: 97
}, {
x: 420,
y: 330,
value: 71
}, {
x: 473,
y: 70,
value: 63
}, {
x: 463,
y: 95,
value: 97
}, {
x: 590,
y: 437,
value: 34
}, {
x: 377,
y: 442,
value: 66
}, {
x: 171,
y: 254,
value: 20
}, {
x: 6,
y: 582,
value: 64
}, {
x: 387,
y: 477,
value: 14
}]
3.为每个数据点设置一个从中心向外灰度渐变的圆
var canvas = document.getElementById('heatmap')
var context = canvas.getContext('2d')
context.clearRect(0, 0, 800, 800)
// radius为该圆的半径,max和min为强弱阈值,可自行设置
var radius = 30
var max = 97
var min = 14
pointsdata.forEach(point => {
let {
x,
y,
value
} = point
// 开始为每个数据点绘制圆,圆心为[x,y],半径为radius
context.beginPath()
context.arc(x, y, radius, 0, 2 * Math.PI)
context.closePath()
// 给圆加上渐变色,圆心至边的渐变色为#000至透明
let radialGradient = context.createRadialGradient(x, y, 0, x, y, radius)
radialGradient.addColorStop(0, 'rgba(0,0,0,1)')
radialGradient.addColorStop(1, 'rgba(0,0,0,0)')
context.fillStyle = radialGradient
// 给圆加上透明度,值为globalAlpha
let globalAlpha = (value - min) / (max - min)
context.globalAlpha = Math.max(Math.min(globalAlpha, 1), 0)
context.fill()
})
此时效果:
4.对图像进行着色
const defaultColorStops = {
'0.0': '#6E32C2',
'0.1': '#1890FF',
'0.2': '#12CCCC',
'0.3': '#80FF73',
'0.4': '#FAFFA8',
'0.5': '#FFC838',
'0.6': '#FF8C12',
'0.7': '#FA541C',
'1.0': '#F51D27',
}
const width = 256
const height = 1
// 创建一条宽为256,高为1的像素带,颜色可自定义。
function getColorPaint() {
let paletteCanvas = document.createElement('canvas')
paletteCanvas.width = width
paletteCanvas.height = height
let ctx = paletteCanvas.getContext('2d')
let linearGradient = ctx.createLinearGradient(0, 0, width, height)
for (const key in defaultColorStops) {
// 绘制彩色渐变色条
linearGradient.addColorStop(key, defaultColorStops[key])
}
ctx.fillStyle = linearGradient
ctx.fillRect(0, 0, width, height)
// 获取色带图像上的像素点。(像素信息为[r, g, b, a, r, g, b, a…],数组中容纳了所有的像素点信息,每个像素点依次排列,每个像素点信息包含r、g、b、a,总共有256个r、g、b、a)
return ctx.getImageData(0, 0, width, height).data
}
let palette = getColorPaint()
// 再获取整张800 * 800画布上的像素信息pointImgData(含灰度渐变圆)(同样,像素信息为[r, g, b, a, r, g, b, a…])
let pointImg = context.getImageData(0, 0, 800, 800)
let pointImgData = pointImg.data
// 获取pointImgData中每个像素点point的alpha值(范围0-255),根据获取到的alpha值,再到色带上一一对应,取色带上对应的像素点(rgb)覆盖到point上。(如当前取到的alpha值为0,则在彩色渐变色带上获取到的便是第一个像素点;若当前取到的alpha值为50,则是第50个像素点。)
for (var i = 3; i < pointImgData.length; i += 4) {
let alpha = pointImgData[i]
let offset = alpha * 4
pointImgData[i - 3] = palette[offset]
pointImgData[i - 2] = palette[offset + 1]
pointImgData[i - 1] = palette[offset + 2]
}
context.putImageData(pointImg, 0, 0)
此时效果:
同样的数据在g2热力图中渲染的效果:
【半径为20】
【半径为30】
同样的数据使用heatmap.js绘制的效果:
【半径为30】
部分文字介绍来源于G2
绘制原理来源于互联网