網上那些炫酷的熱力圖是如何繪製的? 相信你也很好奇,本文將以 canvas 做爲繪圖示例來說解熱力圖的原理。css
咱們常常遇到透明度的概念,如 CSS 中的 opacity 屬性、rgba 顏色中的 alpha 變量、canvas 中的 globalAlpha 屬性等。html
它們的取值範圍通常是 0-1 之間,0 表示徹底透明,1 表示不透明,值越小,越透明。git
思考一個問題: 透明度爲 0.2 的矩形跟透明度爲 0.6 的矩形疊加後的透明度爲多少?github
結果能夠看如下示例,經過 canvas 的 getImageData 方法輸出了疊加後的透明度(值除以 255 便可)canvas
See the Pen canvas-opacity by linghuam (@linghuam) on CodePen.api
不少人的第一感受也許是 0.8,其實這是一種想固然的理解。正確的思路以下:數組
假設把透明度理解成玻璃的透光性,這樣 alpha=0.2 表示一束光照射到玻璃上,有 20% 的光線被反射回來(這一部分光線會進入你的眼睛),80% 穿透過去,這時咱們看到的東西就會很模糊。同理,alpha=0 表示光線所有穿透過去,因此咱們什麼都看不見,alpha=1 表示光線所有被反射,因此咱們能看見所有。ui
那麼 alpha=0.2 和 alpha=0.6 的疊加至關於兩塊玻璃疊加,第一塊玻璃有 80% 光線穿透, 第二塊在第一塊穿透過的光線中,有 40% 光線穿透,這樣穿過兩塊玻璃後被反射多少光線呢,計算方法以下:1*0.2 + (1-0.2)*0.6 = 0.68
, 因此最後的透明度是 0.68 而不是 0.8。this
下面一篇文章總結了其計算公式:兩個半透明顏色色的疊加計算方法spa
其實熱力圖就是根據透明度的大小和疊加來渲染的。
首先咱們的數據集是一個對象數組,每一個元素包含了 { x, y, value } 屬性。咱們首先從這一組值中找出 value 最大值 max,而後用 value/max 的值來表示透明度,這樣咱們能夠在畫布中繪製不一樣透明度的小圓圈,起初這些圓圈都是黑白的,因此下一步須要根據不一樣透明度來進行着色處理。
經過查看 mapv 源碼發現,它實現了一個 Intensity 類用來對不一樣透明度實現一個漸變色。
首先他建立了一個 256 * 1 的 canvas ,而後利用 canvas 的 createLinearGradient 來將漸變色填充進去,這樣一個透明度值就能夠對應 canvas 上的一個顏色值,經過 getImageData 方法就能夠根據透明度來取對應的顏色值。
截取部分核心代碼:
// 建立一個 256*1 的 canvas 並填充漸變色
Intensity.prototype.initPalette = function () {
var gradient = this.gradient;
var canvas = new Canvas(256, 1);
var paletteCtx = this.paletteCtx = canvas.getContext('2d');
var lineGradient = paletteCtx.createLinearGradient(0, 0, 256, 1);
for (var key in gradient) {
lineGradient.addColorStop(parseFloat(key), gradient[key]);
}
paletteCtx.fillStyle = lineGradient;
paletteCtx.fillRect(0, 0, 256, 1);
}
// 經過透明度值取到對應的顏色
Intensity.prototype.getImageData = function (value) {
var imageData = this.paletteCtx.getImageData(0, 0, 256, 1).data;
if (value === undefined) {
return imageData;
}
var max = this.max;
var min = this.min;
if (value > max) {
value = max;
}
if (value < min) {
value = min;
}
var index = Math.floor((value - min) / (max - min) * (256 - 1)) * 4;
return [imageData[index], imageData[index + 1], imageData[index + 2], imageData[index + 3]];
}
複製代碼
固然,爲了熱力圖更好看,做者用了 canvas 的 shadowBlur 來實現一個邊緣模糊效果。
至此,熱力圖的實現原理就介紹完了,下面是我根據這個原理作的一個小 Demo:
See the Pen canvas-heatmap by linghuam (@linghuam) on CodePen.