heatmap.js 一個用canvas畫熱力圖的利器


最近研究了一下百度 淘寶 的點擊區域熱力圖顯示效果!覺有很有趣,於是Google了一下,發現此文不錯,就轉了一下!

轉自:http://1.aisensiy.sinaapp.com/heatmapjs/

以下原文:

  於是就想找一找熱力圖的算法。找到了很多生成熱力圖的工具,它們的算法幾乎是一致的,都是首先用alpha透明度方式畫發散的圓在頁面上,然后利用一個調色板,把對應的透明度改成相應的顏色即可。

一個很不錯的中文的算法介紹在這里 淺談Heatmap

一個英文的在這里 How to Make Heat Map

雖說我本來打算的是找到算法自己去實現一下的,但是一不小心我發現萬能的google在搜索記錄里面給了我一個heatmap.js的鏈接。我好奇的點進去發現這就是我所需要實現的東西…

heatmap.js可以使用canvas畫出來一張漂亮的heatmap。更重要的是它支持數據的動態添加。比如,上圖的演示就是一個利用mousemove事件生成heatmap的例子。它會自動的刷新canvas,實時顯示鼠標運動的heatmap。

打開heatmap.js發現里面的代碼並不多,但是真的很精悍。

頁面代碼請點擊這里[heatmap.js],下面我做一個code的分析吧,看了那么久了,寫下來一方面是自己加深記憶,另一方面就是可以更好的理清思路吧。[寫就是為了更好的思考]么。

code中包含兩個主要的對象,store heatmap。store是heatmap的數據部分,算是model吧。而heatmap則是真正繪制圖像的對象。heatmap部分可以被配置,可以自定義很多的內容,尤其是配色也是可以配置的,那么我們除了做出來正真的heatmap的效果之外還可以做出來各種各樣不錯的效果的。

首先看看存儲部分吧,比較簡單,注釋也比較清楚。

復制代碼
 1 // store object constructor
2 // a heatmap contains a store
3 // the store has to know about the heatmap
4 // in order to trigger heatmap updates when
5 // datapoints get added
6 function store(hmap){
7 var _ = {
8 // data is a two dimensional array
9 // a datapoint gets saved as data[point-x-value][point-y-value]
10 // the value at [point-x-value][point-y-value]
11 // is the occurrence of the datapoint
12 data: [],
13 // tight coupling of the heatmap object
14 heatmap: hmap
15 };
16 // the max occurrence - the heatmaps radial gradient
17 // alpha transition is based on it
18 this.max = 1;
19 this.get = function(key){
20 return _[key];
21 },
22 this.set = function(key, value){
23 _[key] = value;
24 };
25 };
復制代碼

 

在model里面,支持一次添加一個數據點。這也是heatmapjs支持實時繪制的關鍵。一旦max值有變化就會重新繪制整個canvas。

復制代碼
addDataPoint: function(x, y){
if(x < 0 || y < 0)
return;
var me = this,
heatmap
= me.get("heatmap"),
data
= me.get("data");
if(!data[x])
data[x]
= [];
if(!data[x][y])
data[x][y]
= 0;
// if count parameter is set increment by count otherwise by 1
data[x][y]+=(arguments.length me.set("data", data);
// do we have a new maximum?
if(me.max < data[x][y]){
me.max
= data[x][y];
// max changed, we need to redraw all existing(lower) datapoints
heatmap.get("actx").clearRect(0,0,heatmap.get("width"),heatmap.get("height"));
for(var one in data)
for(var two in data[one])
heatmap.drawAlpha(one, two, data[one][two]);
// @TODO
// implement feature
// heatmap.drawLegend(); ?
return;
}
heatmap.drawAlpha(x, y, data[x][y]);
},
復制代碼

 

下面就是畫的部分了。這里是最重要的兩個方法,drawAlpha colorize

復制代碼
drawAlpha: function(x, y, count){
// storing the variables because they will be often used
var me = this,
r1
= me.get("radiusIn"),
r2
= me.get("radiusOut"),
ctx
= me.get("actx"),
max
= me.get("max"),
// create a radial gradient with the defined parameters.
// we want to draw an alphamap
rgr = ctx.createRadialGradient(x,y,r1,x,y,r2),
xb
= x-r2, yb = y-r2, mul = 2*r2;
// the center of the radial gradient has .1 alpha value
rgr.addColorStop(0, 'rgba(0,0,0,'+((count)?(count/me.store.max):'0.1')+')');
// and it fades out to 0
rgr.addColorStop(1, 'rgba(0,0,0,0)');
// drawing the gradient
ctx.fillStyle = rgr;
ctx.fillRect(xb,yb,mul,mul);
// finally colorize the area
me.colorize(xb,yb);
},
復制代碼

 

策略很簡單,

rgr.addColorStop(0, 'rgba(0,0,0,'+((count)?(count/me.store.max):'0.1')+')');
// and it fades out to 0
rgr.addColorStop(1, 'rgba(0,0,0,0)');

 

利用當前點的count除以最大的count獲取的結果做為alpha值。然后做一個RadialGradient畫出來這個圖就可以了。那么由於多個相近的點aphla效果的疊加就可以獲取想要的效果了。這里就是canvas的nb之處了,看別的語言實現都是采用將一個這樣的png圖片畫到畫板上,但是canvas就可以直接實現這個效果。

有了這幅aphla版本的heatmap 我們利用一個配送版做着色就大功告成了。

這里又用到了上面所說的canvas的nb之處,在通常需要一個圖片作為配色板的時候canvas可以自己做出來一個緩存起來。

復制代碼
initColorPalette: function(){
var me = this,
canvas
= document.createElement("canvas");
canvas.width
= "1";
canvas.height
= "256";
var ctx = canvas.getContext("2d"),
grad
= ctx.createLinearGradient(0,0,1,256),
gradient
= me.get("gradient");
for(var x in gradient){
grad.addColorStop(x, gradient[x]);
}
ctx.fillStyle
= grad;
ctx.fillRect(
0,0,1,256);
//這里太強大了,緩存了我的畫板數據,然后刪除了畫板
me.set("gradient", ctx.getImageData(0,0,1,256).data);
delete canvas;
delete grad;
delete ctx;
},
復制代碼

 

這種方式也給我們實現各種各樣的配色提供了方便,我們只需要改變那個 **gradient** 就可以了。

復制代碼
for(var i=3; i < length; i+=4){         // [0] -> r, [1] -> g, [2] -> b, [3] -> alpha
var alpha = imageData[i],
offset
= alpha*4;
if(!offset)
continue;
// we ve started with i=3
// set the new r, g and b values
// 根據透明度選擇配色板上的配色
imageData[i-3]=palette[offset];
imageData[i
-2]=palette[offset+1];
imageData[i
-1]=palette[offset+2];
// we want the heatmap to have a gradient from transparent to the colors
// as long as alpha is lower than the defined opacity (maximum),
// we'll use the alpha value
imageData[i] = (alpha < opacity)?alpha:opacity;
}
復制代碼

 

還是很簡練的吧,看到heatmap.js的風格,真的像是在看一個不錯的藝術品一樣。強烈推薦一看~


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com