HeatmapLayer.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. var zrUtil = require("zrender/lib/core/util");
  2. /**
  3. * @file defines echarts Heatmap Chart
  4. * @author Ovilia (me@zhangwenli.com)
  5. * Inspired by https://github.com/mourner/simpleheat
  6. *
  7. * @module
  8. */
  9. var GRADIENT_LEVELS = 256;
  10. /**
  11. * Heatmap Chart
  12. *
  13. * @class
  14. */
  15. function Heatmap() {
  16. var canvas = zrUtil.createCanvas();
  17. this.canvas = canvas;
  18. this.blurSize = 30;
  19. this.pointSize = 20;
  20. this.maxOpacity = 1;
  21. this.minOpacity = 0;
  22. this._gradientPixels = {};
  23. }
  24. Heatmap.prototype = {
  25. /**
  26. * Renders Heatmap and returns the rendered canvas
  27. * @param {Array} data array of data, each has x, y, value
  28. * @param {number} width canvas width
  29. * @param {number} height canvas height
  30. */
  31. update: function (data, width, height, normalize, colorFunc, isInRange) {
  32. var brush = this._getBrush();
  33. var gradientInRange = this._getGradient(data, colorFunc, 'inRange');
  34. var gradientOutOfRange = this._getGradient(data, colorFunc, 'outOfRange');
  35. var r = this.pointSize + this.blurSize;
  36. var canvas = this.canvas;
  37. var ctx = canvas.getContext('2d');
  38. var len = data.length;
  39. canvas.width = width;
  40. canvas.height = height;
  41. for (var i = 0; i < len; ++i) {
  42. var p = data[i];
  43. var x = p[0];
  44. var y = p[1];
  45. var value = p[2]; // calculate alpha using value
  46. var alpha = normalize(value); // draw with the circle brush with alpha
  47. ctx.globalAlpha = alpha;
  48. ctx.drawImage(brush, x - r, y - r);
  49. }
  50. if (!canvas.width || !canvas.height) {
  51. // Avoid "Uncaught DOMException: Failed to execute 'getImageData' on
  52. // 'CanvasRenderingContext2D': The source height is 0."
  53. return canvas;
  54. } // colorize the canvas using alpha value and set with gradient
  55. var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
  56. var pixels = imageData.data;
  57. var offset = 0;
  58. var pixelLen = pixels.length;
  59. var minOpacity = this.minOpacity;
  60. var maxOpacity = this.maxOpacity;
  61. var diffOpacity = maxOpacity - minOpacity;
  62. while (offset < pixelLen) {
  63. var alpha = pixels[offset + 3] / 256;
  64. var gradientOffset = Math.floor(alpha * (GRADIENT_LEVELS - 1)) * 4; // Simple optimize to ignore the empty data
  65. if (alpha > 0) {
  66. var gradient = isInRange(alpha) ? gradientInRange : gradientOutOfRange; // Any alpha > 0 will be mapped to [minOpacity, maxOpacity]
  67. alpha > 0 && (alpha = alpha * diffOpacity + minOpacity);
  68. pixels[offset++] = gradient[gradientOffset];
  69. pixels[offset++] = gradient[gradientOffset + 1];
  70. pixels[offset++] = gradient[gradientOffset + 2];
  71. pixels[offset++] = gradient[gradientOffset + 3] * alpha * 256;
  72. } else {
  73. offset += 4;
  74. }
  75. }
  76. ctx.putImageData(imageData, 0, 0);
  77. return canvas;
  78. },
  79. /**
  80. * get canvas of a black circle brush used for canvas to draw later
  81. * @private
  82. * @returns {Object} circle brush canvas
  83. */
  84. _getBrush: function () {
  85. var brushCanvas = this._brushCanvas || (this._brushCanvas = zrUtil.createCanvas()); // set brush size
  86. var r = this.pointSize + this.blurSize;
  87. var d = r * 2;
  88. brushCanvas.width = d;
  89. brushCanvas.height = d;
  90. var ctx = brushCanvas.getContext('2d');
  91. ctx.clearRect(0, 0, d, d); // in order to render shadow without the distinct circle,
  92. // draw the distinct circle in an invisible place,
  93. // and use shadowOffset to draw shadow in the center of the canvas
  94. ctx.shadowOffsetX = d;
  95. ctx.shadowBlur = this.blurSize; // draw the shadow in black, and use alpha and shadow blur to generate
  96. // color in color map
  97. ctx.shadowColor = '#000'; // draw circle in the left to the canvas
  98. ctx.beginPath();
  99. ctx.arc(-r, r, this.pointSize, 0, Math.PI * 2, true);
  100. ctx.closePath();
  101. ctx.fill();
  102. return brushCanvas;
  103. },
  104. /**
  105. * get gradient color map
  106. * @private
  107. */
  108. _getGradient: function (data, colorFunc, state) {
  109. var gradientPixels = this._gradientPixels;
  110. var pixelsSingleState = gradientPixels[state] || (gradientPixels[state] = new Uint8ClampedArray(256 * 4));
  111. var color = [0, 0, 0, 0];
  112. var off = 0;
  113. for (var i = 0; i < 256; i++) {
  114. colorFunc[state](i / 255, true, color);
  115. pixelsSingleState[off++] = color[0];
  116. pixelsSingleState[off++] = color[1];
  117. pixelsSingleState[off++] = color[2];
  118. pixelsSingleState[off++] = color[3];
  119. }
  120. return pixelsSingleState;
  121. }
  122. };
  123. var _default = Heatmap;
  124. module.exports = _default;