index.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import { lru } from '../../../utils/lru';
  2. import { parseGradient } from './gradient';
  3. function newCanvas(createCanvas, width, height) {
  4. const c = createCanvas ? createCanvas() : document.createElement('canvas');
  5. c.width = width;
  6. c.height = height;
  7. return c;
  8. }
  9. /**
  10. * Get a point with template.
  11. * @param radius
  12. * @param blurFactor
  13. * @returns
  14. */
  15. const getPointTemplate = lru((radius, blurFactor, createCanvas) => {
  16. const tplCanvas = newCanvas(createCanvas, radius * 2, radius * 2);
  17. const tplCtx = tplCanvas.getContext('2d');
  18. const x = radius;
  19. const y = radius;
  20. if (blurFactor === 1) {
  21. tplCtx.beginPath();
  22. tplCtx.arc(x, y, radius, 0, 2 * Math.PI, false);
  23. tplCtx.fillStyle = 'rgba(0,0,0,1)';
  24. tplCtx.fill();
  25. }
  26. else {
  27. const gradient = tplCtx.createRadialGradient(x, y, radius * blurFactor, x, y, radius);
  28. gradient.addColorStop(0, 'rgba(0,0,0,1)');
  29. gradient.addColorStop(1, 'rgba(0,0,0,0)');
  30. tplCtx.fillStyle = gradient;
  31. tplCtx.fillRect(0, 0, 2 * radius, 2 * radius);
  32. }
  33. return tplCanvas;
  34. }, (radius) => `${radius}`);
  35. /**
  36. * Get a color palette with len = 256 base on gradient.
  37. * @param gradientConfig
  38. * @returns
  39. */
  40. function getColorPalette(gradientConfig, createCanvas) {
  41. const paletteCanvas = newCanvas(createCanvas, 256, 1);
  42. const paletteCtx = paletteCanvas.getContext('2d');
  43. const gradient = paletteCtx.createLinearGradient(0, 0, 256, 1);
  44. parseGradient(gradientConfig).forEach(([r, c]) => {
  45. gradient.addColorStop(r, c);
  46. });
  47. paletteCtx.fillStyle = gradient;
  48. paletteCtx.fillRect(0, 0, 256, 1);
  49. return paletteCtx.getImageData(0, 0, 256, 1).data;
  50. }
  51. /**
  52. * Draw all circle with alpha.
  53. */
  54. function drawAlpha(shadowCtx, min, max, data, options, createCanvas) {
  55. const { blur } = options;
  56. let len = data.length;
  57. while (len--) {
  58. const { x, y, value: v, radius } = data[len];
  59. // Ff value is bigger than max, use max as value.
  60. const value = Math.min(v, max);
  61. const rectX = x - radius;
  62. const rectY = y - radius;
  63. const tpl = getPointTemplate(radius, 1 - blur, createCanvas);
  64. // Value from minimum / value range, => [0, 1].
  65. const templateAlpha = (value - min) / (max - min);
  66. // Small values are not visible because globalAlpha < .001 cannot be read from imageData.
  67. shadowCtx.globalAlpha = Math.max(templateAlpha, 0.001);
  68. shadowCtx.drawImage(tpl, rectX, rectY);
  69. }
  70. return shadowCtx;
  71. }
  72. function colorize(shadowCtx, maxWidth, maxHeight, palette, options) {
  73. const { minOpacity, opacity, maxOpacity, useGradientOpacity } = options;
  74. const x = 0;
  75. const y = 0;
  76. const width = maxWidth;
  77. const height = maxHeight;
  78. const img = shadowCtx.getImageData(x, y, width, height);
  79. const imgData = img.data;
  80. const len = imgData.length;
  81. for (let i = 3; i < len; i += 4) {
  82. const alpha = imgData[i];
  83. const offset = alpha * 4;
  84. if (!offset) {
  85. continue;
  86. }
  87. // Should be in [min, max], min >= 0.
  88. const finalAlpha = opacity || Math.max(0, Math.min(maxOpacity, Math.max(minOpacity, alpha)));
  89. // Update rgba.
  90. imgData[i - 3] = palette[offset];
  91. imgData[i - 2] = palette[offset + 1];
  92. imgData[i - 1] = palette[offset + 2];
  93. imgData[i] = useGradientOpacity ? palette[offset + 3] : finalAlpha;
  94. }
  95. return img;
  96. }
  97. /**
  98. * Render a heatmap with canvas.
  99. * See [heatmap.js](https://github.com/pa7/heatmap.js/blob/master/src/renderer/canvas2d.js).
  100. */
  101. export function HeatmapRenderer(width, height, min, max, data, options, createCanvas) {
  102. const opts = Object.assign({ blur: 0.85, minOpacity: 0, opacity: 0.6, maxOpacity: 1, gradient: [
  103. [0.25, 'rgb(0,0,255)'],
  104. [0.55, 'rgb(0,255,0)'],
  105. [0.85, 'yellow'],
  106. [1.0, 'rgb(255,0,0)'],
  107. ] }, options);
  108. opts.minOpacity *= 255;
  109. opts.opacity *= 255;
  110. opts.maxOpacity *= 255;
  111. const shadowCanvas = newCanvas(createCanvas, width, height);
  112. const shadowCtx = shadowCanvas.getContext('2d');
  113. const palette = getColorPalette(opts.gradient, createCanvas);
  114. shadowCtx.clearRect(0, 0, width, height);
  115. drawAlpha(shadowCtx, min, max, data, opts, createCanvas);
  116. const img = colorize(shadowCtx, width, height, palette, opts);
  117. const canvas = newCanvas(createCanvas, width, height);
  118. const ctx = canvas.getContext('2d');
  119. ctx.putImageData(img, 0, 0);
  120. return ctx;
  121. }
  122. //# sourceMappingURL=index.js.map