学习了canvas
的基础知识后,知道怎么绘制一个图形及添加渐变效果了,又大概阅读了一下heatmap.js
的源码,为了消化heatmap.js
的源码及熟悉下canvas
的知识,自己动手将heatmap
重新写了一个精简版的,方便查看heatmap
的实现原理。代码只是简单的实现下热力图的实现,加深下热力图的渲染逻辑。
封装对象
实现JS框架,首先需要对代码进行封装,既要实现代码的可扩展性和实用性,又不能污染全局变量,通过阅读了一些源码后,发现封装工具都有一套类似的代码,可以将该代码作为封装工具集的模板,其具体如下所示:
首先是创建使用一个立即执行的函数,防止污染全局变量:
;(function (name, context, factory) {})("h338", this, function () {return obj})
前面的;
是为了压缩代码为一行时,防止有些代码没以;
结束,导致语法错误。比如立即执行函数前没有;
时,前面的代码刚好是类似这样的代码:if(true)return
,这样将会导致该if
语句结束时,返回的是一个obj
对象。
创建完立即执行函数后,就是创建一个类来封装相关的属性和方法了,如下所示:
;(function (name, context, factory) {
// Supports UMD. AMD, CommonJS/Node.js and browser context
if (typeof module !== "undefined" && module.exports) {//node/CommonJS模式
module.exports = factory();
} else if (typeof define === "function" && define.amd) {//AMD模式
define(factory);
} else {//UMD模式
context[name] = factory();
}
})("h338", this, function () {
//创建一个Heatmap类
var Heatmap = (function HeatmapClosure() {
//Heatmap构造函数
function Heatmap(config) {
}
//添加外部调用的方法
Heatmap.prototype = {
//动态添加热力图
addData: function () {
console.log('添加数据')
},
}
return Heatmap;
})();
// 核心代码,创建heatmap对象
var heatmapFactory = {
create: function(config) {
return new Heatmap(config);
},
};
return heatmapFactory;
});
添加一个初始化的create
方法,该方法返回构造函数的实例。然后通过原型对象来添加外部调用的方法。
创建热力图小圆圈
热力图都是由一个个小圆圈组成后,根据圆半径来生成一个圆圈渐变的图像,然后再绘制到指定的位置上。使用createRadialGradient
可以绘制一个径向渐变的图形,如下所示:
//绘制一个一个小圆
drawCircle:function(value){
var radius = this.radius;
var x = radius;
var y = radius;
var tplCanvas = document.createElement('canvas');
var tplshadowCtx = tplCanvas.getContext('2d');
tplCanvas.width = tplCanvas.height = radius*2;
var gradient = tplshadowCtx.createRadialGradient(x,y,radius * 0.1,x,y,radius);
gradient.addColorStop(0, 'rgba(0,0,0,1)');
gradient.addColorStop(1, 'rgba(0,0,0,0)');
tplshadowCtx.fillStyle = gradient;
tplshadowCtx.fillRect(0, 0, 2*radius, 2*radius);
this._circleTemp[value] = tplCanvas;
return tplCanvas;
},
创建调色板
使用createLinearGradient
用来创建一个线性渐变图形,大小为256*1,创建的调色板主要是用于后面的着色方法,将前面绘制的小圆圈染成调色板上面的颜色。具体代码如下所示:
//调色板
getColorPalette:function () {
var paletteCanvas = document.createElement('canvas');//创建画布
var paletteshadowCtx = paletteCanvas.getContext('2d');//获取画布的上下文
paletteCanvas.width = 256;
paletteCanvas.height = 1;
//线性渐变对象
var gradient = paletteshadowCtx.createLinearGradient(0, 0, 256, 1);
//热度默认颜色值,从外到里的颜色
var gradientConfig = { 0.25: "rgb(0,0,255)", 0.55: "rgb(0,255,0)", 0.85: "yellow", 1.0: "rgb(255,0,0)"};
for (var key in gradientConfig) {
//插入断点使渐变变成一段一段的色块
gradient.addColorStop(key, gradientConfig[key]);//添加渐变点
}
paletteshadowCtx.fillStyle = gradient;//上下文的填充样式
paletteshadowCtx.fillRect(0, 0, 256, 1);//绘制一个宽为256,高为1的矩形
//返回一个Uint8ClampedArray类型的图片数据
return paletteshadowCtx.getImageData(0, 0, 256, 1).data;
}
着色
着色算是热力图渲染最核心的一步,首先将绘制的区域生成一张图片,取出所有的相素点,然后再通过前面创建的调色板修改颜色,修改后的颜色如下所示:
//着色
colorize:function(){
var x = this._renderBoundaries[0];
var y = this._renderBoundaries[1];
var width = this._renderBoundaries[2] - x;
var height = this._renderBoundaries[3] - y;
var img = this.shadowCtx.getImageData(x, y, width, height);
var imgData = img.data;
var len = imgData.length;
var palette = this.getColorPalette();
for (var i = 3; i < len; i+= 4) {
var alpha = imgData[i];
var offset = alpha * 4;
if (!offset) {//只渲染有颜色的相素
continue;
}
imgData[i-3] = palette[offset];
imgData[i-2] = palette[offset + 1];
imgData[i-1] = palette[offset + 2];
imgData[i] = palette[offset + 3];
}
img.data = imgData;
this.ctx.putImageData(img, x, y);
this._renderBoundaries = [1000, 1000, 0, 0];
},