熱力圖長(zhǎng)什么樣?
(截圖來(lái)自g2熱力圖)

什么是熱力圖?
熱力圖是數(shù)據(jù)可視化項(xiàng)目中,比較常用的顯示方式。通過(guò)顏色變化程度,他可以直觀反應(yīng)出熱點(diǎn)分布,區(qū)域聚集等數(shù)據(jù)信息。
“熱力圖”一詞最初是由軟件設(shè)計(jì)師Cormac Kinney于1991年提出并創(chuàng)造的,用來(lái)描述一個(gè)2D顯示實(shí)時(shí)金融市場(chǎng)信息。最開(kāi)始的熱力圖,是矩形色塊加上顏色編碼。經(jīng)過(guò)多年的演化,習(xí)語(yǔ)上的熱力圖,如今更規(guī)范,更被大多數(shù)人理解的是這種經(jīng)過(guò)平滑模糊過(guò)的熱力圖譜。
熱力圖一般是基于離散點(diǎn)、線或面的分析與表達(dá),或者基于連續(xù)表面的密度分析得到的。
如何實(shí)現(xiàn)熱力圖?
大致分為三個(gè)步驟:
1.為每個(gè)數(shù)據(jù)點(diǎn)設(shè)置一個(gè)從中心向外灰度漸變的圓。
2.利用灰度可以疊加的原理,計(jì)算每個(gè)像素點(diǎn)數(shù)據(jù)交叉疊加得到的灰度值。
3.根據(jù)每個(gè)像素計(jì)算得到的灰度值,在一條彩色色帶中進(jìn)行顏色映射,最后對(duì)圖像進(jìn)行著色,得到熱力圖。
代碼實(shí)現(xiàn)過(guò)程:
1.創(chuàng)建800*800的畫(huà)布
<canvas id="heatmap" width="800" height="800" style="border:1px solid #000000;"></canvas>
2.聲明一個(gè)長(zhǎng)度為10的list,x、y代表坐標(biāo)軸,value代表該點(diǎn)的值。比如聲音分貝。
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.為每個(gè)數(shù)據(jù)點(diǎn)設(shè)置一個(gè)從中心向外灰度漸變的圓
var canvas = document.getElementById('heatmap')
var context = canvas.getContext('2d')
context.clearRect(0, 0, 800, 800)
// radius為該圓的半徑,max和min為強(qiáng)弱閾值,可自行設(shè)置
var radius = 30
var max = 97
var min = 14
pointsdata.forEach(point => {
let {
x,
y,
value
} = point
// 開(kāi)始為每個(gè)數(shù)據(jù)點(diǎn)繪制圓,圓心為[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()
})
此時(shí)效果:

4.對(duì)圖像進(jìn)行著色
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
// 創(chuàng)建一條寬為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)
// 獲取色帶圖像上的像素點(diǎn)。(像素信息為[r, g, b, a, r, g, b, a…],數(shù)組中容納了所有的像素點(diǎn)信息,每個(gè)像素點(diǎn)依次排列,每個(gè)像素點(diǎn)信息包含r、g、b、a,總共有256個(gè)r、g、b、a)
return ctx.getImageData(0, 0, width, height).data
}
let palette = getColorPaint()
// 再獲取整張800 * 800畫(huà)布上的像素信息pointImgData(含灰度漸變圓)(同樣,像素信息為[r, g, b, a, r, g, b, a…])
let pointImg = context.getImageData(0, 0, 800, 800)
let pointImgData = pointImg.data
// 獲取pointImgData中每個(gè)像素點(diǎn)point的alpha值(范圍0-255),根據(jù)獲取到的alpha值,再到色帶上一一對(duì)應(yīng),取色帶上對(duì)應(yīng)的像素點(diǎn)(rgb)覆蓋到point上。(如當(dāng)前取到的alpha值為0,則在彩色漸變色帶上獲取到的便是第一個(gè)像素點(diǎn);若當(dāng)前取到的alpha值為50,則是第50個(gè)像素點(diǎn)。)
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)
此時(shí)效果:

同樣的數(shù)據(jù)在g2熱力圖中渲染的效果:
【半徑為20】

【半徑為30】

同樣的數(shù)據(jù)使用heatmap.js繪制的效果:
【半徑為30】

部分文字介紹來(lái)源于G2
繪制原理來(lái)源于互聯(lián)網(wǎng)