最近做一个项目,需要绘制热力图,但是发觉现成的库封装都很不灵活
heatmap.js
百度Echarts
等都有封装
参考百度echarts的热力图插件,我整理了一下绘制热力图的思路
数据构成
[[x, y, cnt], [x, y, cnt]]
// x y 表示这个点在canvas上的位置
对canvas的简单封装
function Canvas(w, h) {
this.canvas = document.createElement('canvas')
this.canvas.width = w
this.canvas.height = h
}
Canvas.prototype.getContext = function() {
return this.canvas.getContext('2d')
}
Canvas.prototype.getCanvas = function(){
return this.canvas
}
创建canvas的每个点的渐变色
function creatHeadtMapCircle(size) {
const shadowBlur = size / 2
const r = size + shadowBlur
const offsetDistance = 1000
const circle = new Canvas(r * 2, r * 2)
let ctx = circle.getContext()
ctx.shadowBlur = shadowBlur
ctx.shadowColor = 'black'
ctx.shadowOffsetX = ctx.shadowOffsetY = offsetDistance
ctx.beginPath()
ctx.arc( r - offsetDistance, r2 - offsetDistance, size, 0, Mathi.PI * 2 , true)
ctx.closePath()
ctx.fill()
return circle
}
绘制单个点的结果
绘制多个点,根据权重值,可以得到如下的图形, 不同的权重 设置不同的透明度
创一个颜色映射
class ColorRange {
constructor (options) {
this.gradient = options.gradient || {
0.25: 'rgba(0, 0, 255, 1)',
0.55: 'rgba(0, 255, 0, 1)',
0.85: 'rgba(255, 255, 0, 1)',
1.0: 'rgba(255, 0, 0, 1)'
}
this.max = options.max || 30
this.min = options.min || 0
this.initPalette()
}
initPalette () {
const gradient = this.gradient
const canvas = new Canvas(256, 1)
const ctx = this.ctx = canvas.getContext()
const lineGradient = ctx.createLinearGradient(0, 0, 256, 1)
for (let key in gradient) {
lineGradient.addColorStop(parseFloat(key), gradient[key])
}
ctx.fillStyle = lineGradient
ctx.fillRect(0, 0, 256, 1)
}
getImageData (v ) {
const imageData = this.ctx,getImageData(0, 0, 256, 1).data
if (v === undefined) return imageData
if (v > this.max) v = this.max
if (v < this.min ) v = this.min
var index = Math.floor((v - min) / (max - min) * 255 ) * 4
return [imageData[index], imageData[index + 1], imageData[index + 2], imageData[index + 3]]
}
}
颜色映射
根据不同的绘图结果把不同的颜色透明度,映射到不同的颜色上,得到色彩图
let colored = sctx.getImageData(0, 0, canvas.width, canvas.height)
let gradient = (new ColorRange()).getImageData()
const opacity = 0.8
for(let i = 0, len = colored.data.length, j; i < len; i += 4) {
j = colored.data[i] * 4
if (colored.data[i] / 256 > opacity) colored.data[i] = 256 * opacity
colored.data[i - 3] = gradient[j]
colored.data[i - 2] = gradient[j + 1]
colored.data[i - 1] = gradient[j + 2]
}
ctx.putImageData(colored, 0, 0)
最后整理成一个快速好用的库,提供给大家