美文网首页
Heatmap.js热力图实现原理

Heatmap.js热力图实现原理

作者: 写前端的大叔 | 来源:发表于2019-12-11 21:15 被阅读0次

Heatmap.js是基于canvas开源的热力图框架,使用该框架可以方便的实现热力图,其效果图如下所示:

热力图.png
最近正好在学习canvas,顺便研究了一下Heatmap的源码,Heatmap的代码还是比较好理解的,代码结构清晰,有很多值得学习的,比如代码的架构,热力图渲染原理、着色等。下面来说下具体的实现过程。讲的不是很清楚,可以看下精简版的代码实现。

架构

Heatmap其架构主要由两部分组成,分别是RendererStore
Renderer:渲染器,主要用于热力图画布的创建,绘制、着色等。
Store:数据管理器,主要用于管理热力图的数据,包括添加数据、更新数据、删除数据、组装渲染器需要的数据格式等。

Heatmap对象

Heatmap对象主要用于构造一个外部调用的对象,将公共方法写在原型对象中,在构造函数中,创建对象的时候,初始化了一个_renderer对象和_store对象,分别用于渲染热力和管理数据。

// 核心代码,创建heatmap对象
var heatmapFactory = {
  create: function(config) {
    return new Heatmap(config);
  },
  register: function(pluginKey, plugin) {
    HeatmapConfig.plugins[pluginKey] = plugin;
  }
};
return heatmapFactory;
//默认属性
var HeatmapConfig = {
  defaultRadius: 40,//半径
  defaultRenderer: 'canvas2d',//默认为2D画布
  defaultGradient: { 0.25: "rgb(0,0,255)", 0.55: "rgb(0,255,0)", 0.85: "yellow", 1.0: "rgb(255,0,0)"}, //热度默认颜色值,从外到里的颜色
  defaultMaxOpacity: 1,//最大透明度
  defaultMinOpacity: 0,//最小透明度
  defaultBlur: .85,//模糊度
  defaultXField: 'x',//x坐标字段名
  defaultYField: 'y',//主坐标字段名
  defaultValueField: 'value', //热度值字段名
  plugins: {}//插件
};

数据管理器

Store是一个数据管理器,用于管理热力图所需要的数据。主要方法如下所示:
_organiseData用于构造渲染器所需要的数据格式,主要用于查找出热力值的最大值和最小值,通过这两个值来决定各热力图的颜色渲染比例。其构造的每个对象如下所示:

          { 
            x: x, 
            y: y,
            value: value, 
            radius: radius,
            min: min,
            max: max 
          }

_unOrganizeData:用于还原_organiseData组装的数据,方便外部获取。其对象格式如下所示:

          {
            x: x,
            y: y,
            radius: radi[x][y],
            value: data[x][y]
          }

_onExtremaChange:min或者max有变化时通知_coordinator执行extremachange方法进行重新渲染。
addData:动态添加数据进行渲染。
setDataMax:设置最大值。
setDataMin:设置最小值。
getData:获取原始数据。

渲染器

Renderer是整个框架中的核心代码,通过该渲染器可以完美的创建出热力图。通过Canvas2dRenderer来创建画布和创建调色板等。

调色板

调色板是通过使用defaultGradient设置的颜色来创建一个256*1的画布,使用createLinearGradient创建渐变的图像,最后将返回一个Uint8ClampedArray类型的图片数据,该是一个包含RGBA像素信息的Uint8ClampedArray,数组中所有的值都是整数,范围是0~255。

//获取颜色调色板
  var _getColorPalette = function(config) {
    var gradientConfig = config.gradient || config.defaultGradient;
    var paletteCanvas = document.createElement('canvas');//创建画布
    var paletteCtx = paletteCanvas.getContext('2d');//获取画布的上下文

    paletteCanvas.width = 256;
    paletteCanvas.height = 1;

    //线性渐变对象
    var gradient = paletteCtx.createLinearGradient(0, 0, 256, 1);
    for (var key in gradientConfig) {
      //插入断点使渐变变成一段一段的色块
      gradient.addColorStop(key, gradientConfig[key]);//添加渐变点
    }

    paletteCtx.fillStyle = gradient;//上下文的填充样式
    paletteCtx.fillRect(0, 0, 256, 1);//绘制一个宽为256,高为1的矩形
    //返回一个Uint8ClampedArray类型的图片数据
    return paletteCtx.getImageData(0, 0, 256, 1).data;
  };

绘制热力图

热力图的绘制是使用_drawAlpha方法来完成的,通过获取到数据的x坐标和y坐标、半径等值来生成一个canvas,然后再更新渲染边界。

 //绘制热点图
    _drawAlpha: function(data) {
      var min = this._min = data.min;
      var max = this._max = data.max;
      var data = data.data || [];
      var dataLen = data.length;
      // on a point basis?
      var blur = 1 - this._blur;

      while(dataLen--) {

        var point = data[dataLen];

        var x = point.x;
        var y = point.y;
        var radius = point.radius;
        // 如果point的值大于最大值,选用max作为绘制的值
        var value = Math.min(point.value, max);
        var rectX = x - radius;
        var rectY = y - radius;
        var shadowCtx = this.shadowCtx;
        var tpl;//获取图片对象
        if (!this._templates[radius]) {//半径相同时,不需要重新生成图片
          this._templates[radius] = tpl = _getPointTemplate(radius, blur);
        } else {
          tpl = this._templates[radius];
        }
        // 设置透明度
        var templateAlpha = (value-min)/(max-min);
        // 小于0.01的图片将无法显示
        shadowCtx.globalAlpha = templateAlpha < .01 ? .01 : templateAlpha;

        //绘制图片
        shadowCtx.drawImage(tpl, rectX, rectY);

        // 更新渲染边界
        if (rectX < this._renderBoundaries[0]) {
            this._renderBoundaries[0] = rectX;
          }
          if (rectY < this._renderBoundaries[1]) {
            this._renderBoundaries[1] = rectY;
          }
          if (rectX + 2*radius > this._renderBoundaries[2]) {
            this._renderBoundaries[2] = rectX + 2*radius;
          }
          if (rectY + 2*radius > this._renderBoundaries[3]) {
            this._renderBoundaries[3] = rectY + 2*radius;
          }

      }
    },

着色器

着色器算是渲染器中最核心的代码,热力图显示不同的颜色都是通过_colorize来完成后,其核心原理是获取画布的RGBA像素信息,再使用调色板的颜色对画布上的RGBA像素信息进行修改,就可以将热力图渲染出不同的颜色了。

_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 maxWidth = this._width;
      var maxHeight = this._height;
      var opacity = this._opacity;
      var maxOpacity = this._maxOpacity;
      var minOpacity = this._minOpacity;
      var useGradientOpacity = this._useGradientOpacity;

      if (x < 0) {
        x = 0;
      }
      if (y < 0) {
        y = 0;
      }
      if (x + width > maxWidth) {
        width = maxWidth - x;
      }
      if (y + height > maxHeight) {
        height = maxHeight - y;
      }

      var img = this.shadowCtx.getImageData(x, y, width, height);
      var imgData = img.data;
      var len = imgData.length;
      var palette = this._palette;


      for (var i = 3; i < len; i+= 4) {
        var alpha = imgData[i];
        var offset = alpha * 4;


        if (!offset) {
          continue;
        }
          debugger

        var finalAlpha;
        if (opacity > 0) {
          finalAlpha = opacity;
        } else {
          if (alpha < maxOpacity) {
            if (alpha < minOpacity) {
              finalAlpha = minOpacity;
            } else {
              finalAlpha = alpha;
            }
          } else {
            finalAlpha = maxOpacity;
          }
        }

        imgData[i-3] = palette[offset];
        imgData[i-2] = palette[offset + 1];
        imgData[i-1] = palette[offset + 2];
        imgData[i] = useGradientOpacity ? palette[offset + 3] : finalAlpha;

      }

      img.data = imgData;
      this.ctx.putImageData(img, x, y);

      this._renderBoundaries = [1000, 1000, 0, 0];

    },

以上只是对代码的初步了解,要想完全了解期思想和原理,还需要花更多的时间来进行研究,至少要自己能快速的模仿一个出来才算真正的吃透了。
个人博客

相关文章

  • 『Cesium 基础』热力图(heatmap.js)

    关注公众号"seeling_GIS", 领取学习视频资料 实现原理 通过 heatmap.js 生成热力图,然后通...

  • Heatmap.js热力图实现原理

    Heatmap.js是基于canvas开源的热力图框架,使用该框架可以方便的实现热力图,其效果图如下所示: 架构 ...

  • cesium加载热力图

    首先,这个原理很简答也很直白,一句话就是使用heatmap.js生成热力图后将图贴到Cesium的相应位置。 1、...

  • Cesium实现热力图绘制

    这次介绍在Cesium上如何绘制热力图,现在比较常用的热力图插件是heatmap.js,但是它仅仅提供了对leaf...

  • 使用heatmap.js实现热力图效果

    heatmap不多说,网上资料一大堆,简单来说就是个实现热力图的js库文件,热力图使用h5的canvas绘制热力图。

  • openlayers入门开发系列之热力图篇

    本篇的重点内容是利用openlayers实现热力图功能,效果图如下: 实现思路 热力图界面设计 热力图点击事件 热...

  • 前端热力图制作

    最近做一个项目,需要绘制热力图,但是发觉现成的库封装都很不灵活heatmap.js百度Echarts等都有封装 参...

  • 借助Postgresql生成热力图

    背景 热力图,在空间数据可视化场景中是一个非常常见的需求。首先看下一个完整的热力图效果。 前端实现热力图的原理可以...

  • heatmap.js来绘制热力图

    因为项目需要绘制地图热力图,然后我就试了一下 leaflet.js以及heatmap.jsheatmap.js主页...

  • Android Classloader热修复

    惯例段子。 阅读本文你可以掌握,热修复的原理和简单实现. 目录Classloader热修复原理热修复代码实现面试知...

网友评论

      本文标题:Heatmap.js热力图实现原理

      本文链接:https://www.haomeiwen.com/subject/tjyagctx.html