function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } /** * @fileOverview heatmap * @author leungwensen@gmail.com */ var _require = require('@antv/attr/lib'), ColorUtil = _require.ColorUtil; // TODO: ColorUtil 独立成包,从 attr 包中抽离 var GeomBase = require('./base'); var Util = require('../util'); var ORIGIN_FIELD = '_origin'; var SHADOW_CANVAS = 'shadowCanvas'; var VALUE_RANGE = 'valueRange'; var IMAGE_SHAPE = 'imageShape'; var MAPPED_DATA = 'mappedData'; var GRAY_SCALE_BLURRED_CANVAS = 'grayScaleBlurredCanvas'; var HEATMAP_SIZE = 'heatmapSize'; var Heatmap = /*#__PURE__*/function (_GeomBase) { _inheritsLoose(Heatmap, _GeomBase); function Heatmap() { return _GeomBase.apply(this, arguments) || this; } var _proto = Heatmap.prototype; /** * get default configuration * @protected * @return {Object} configuration */ _proto.getDefaultCfg = function getDefaultCfg() { var cfg = _GeomBase.prototype.getDefaultCfg.call(this); cfg.type = 'heatmap'; cfg.paletteCache = {}; // cfg.shapeType = 'heatmap'; return cfg; }; _proto._prepareRange = function _prepareRange() { var self = this; var data = self.get(MAPPED_DATA); var colorAttr = self.getAttr('color'); var colorField = colorAttr.field; var min = Infinity; var max = -Infinity; data.forEach(function (row) { var value = row[ORIGIN_FIELD][colorField]; if (value > max) { max = value; } if (value < min) { min = value; } }); if (min === max) { min = max - 1; } var range = [min, max]; self.set(VALUE_RANGE, range); }; _proto._prepareSize = function _prepareSize() { var self = this; var radius = self.getDefaultValue('size'); if (!Util.isNumber(radius)) { radius = self._getDefaultSize(); } var styleOptions = self.get('styleOptions'); var blur = styleOptions && Util.isObject(styleOptions.style) ? styleOptions.style.blur : null; if (!Util.isFinite(blur) || blur === null) { blur = radius / 2; } self.set(HEATMAP_SIZE, { blur: blur, radius: radius }); }; _proto._getDefaultSize = function _getDefaultSize() { var self = this; var position = self.getAttr('position'); var coord = self.get('coord'); var radius = Math.min(coord.width / (position.scales[0].ticks.length * 4), coord.height / (position.scales[1].ticks.length * 4)); return radius; }; _proto._colorize = function _colorize(img) { var self = this; var colorAttr = self.getAttr('color'); var pixels = img.data; var paletteCache = self.get('paletteCache'); for (var i = 3; i < pixels.length; i += 4) { var alpha = pixels[i]; // get gradient color from opacity value if (alpha) { var palette = void 0; if (paletteCache[alpha]) { palette = paletteCache[alpha]; } else { palette = ColorUtil.rgb2arr(colorAttr.gradient(alpha / 256)); paletteCache[alpha] = palette; } // const palette = colorUtil.rgb2arr(colorAttr.gradient(alpha / 256)); pixels[i - 3] = palette[0]; pixels[i - 2] = palette[1]; pixels[i - 1] = palette[2]; pixels[i] = alpha; } } }; _proto._prepareGreyScaleBlurredCircle = function _prepareGreyScaleBlurredCircle(r, blur) { var self = this; var circleCanvas = self.get(GRAY_SCALE_BLURRED_CANVAS); if (!circleCanvas) { circleCanvas = document.createElement('canvas'); self.set(GRAY_SCALE_BLURRED_CANVAS, circleCanvas); } var r2 = r + blur; var ctx = circleCanvas.getContext('2d'); circleCanvas.width = circleCanvas.height = r2 * 2; ctx.clearRect(0, 0, circleCanvas.width, circleCanvas.height); // ctx.shadowOffsetX = ctx.shadowOffsetY = r2 * 2; ctx.shadowOffsetX = ctx.shadowOffsetY = r2 * 2; ctx.shadowBlur = blur; ctx.shadowColor = 'black'; ctx.beginPath(); ctx.arc(-r2, -r2, r, 0, Math.PI * 2, true); ctx.closePath(); ctx.fill(); }; _proto._drawGrayScaleBlurredCircle = function _drawGrayScaleBlurredCircle(x, y, r, alpha, ctx) { var self = this; var circleCanvas = self.get(GRAY_SCALE_BLURRED_CANVAS); ctx.globalAlpha = alpha; ctx.drawImage(circleCanvas, x - r, y - r); }; _proto._getShadowCanvasCtx = function _getShadowCanvasCtx() { var self = this; var canvas = self.get(SHADOW_CANVAS); if (!canvas) { canvas = document.createElement('canvas'); self.set(SHADOW_CANVAS, canvas); } var coord = self.get('coord'); if (coord) { canvas.width = coord.width; canvas.height = coord.height; } return canvas.getContext('2d'); }; _proto._clearShadowCanvasCtx = function _clearShadowCanvasCtx() { var ctx = this._getShadowCanvasCtx(); ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); }; _proto._getImageShape = function _getImageShape() { var self = this; var imageShape = self.get(IMAGE_SHAPE); if (imageShape) { return imageShape; } var container = self.get('container'); imageShape = container.addShape('Image', {}); self.set(IMAGE_SHAPE, imageShape); return imageShape; }; _proto.clear = function clear() { // @2019-02-28 by blue.lb 由于设置了SHADOW_CANVAS作为像素缓存canvas,每次销毁chart时,也需要清除该缓冲区 this._clearShadowCanvasCtx(); _GeomBase.prototype.clear.call(this); }; _proto.drawWithRange = function drawWithRange(range) { var self = this; // canvas size var _self$get = self.get('coord'), start = _self$get.start, end = _self$get.end, width = _self$get.width, height = _self$get.height; // value, range, etc var valueField = self.getAttr('color').field; var size = self.get(HEATMAP_SIZE); // prepare shadow canvas context self._clearShadowCanvasCtx(); var ctx = self._getShadowCanvasCtx(); // filter data var data = self.get(MAPPED_DATA); if (range) { data = data.filter(function (row) { return row[ORIGIN_FIELD][valueField] <= range[1] && row[ORIGIN_FIELD][valueField] >= range[0]; }); } // step1. draw points with shadow var scale = self._getScale(valueField); for (var i = 0; i < data.length; i++) { var obj = data[i]; var cfg = self.getDrawCfg(obj); var alpha = scale.scale(obj[ORIGIN_FIELD][valueField]); self._drawGrayScaleBlurredCircle(cfg.x - start.x, cfg.y - end.y, size.radius + size.blur, alpha, ctx); } // step2. convert pixels var colored = ctx.getImageData(0, 0, width, height); self._clearShadowCanvasCtx(); self._colorize(colored); ctx.putImageData(colored, 0, 0); var imageShape = self._getImageShape(); imageShape.attr('x', start.x); imageShape.attr('y', end.y); imageShape.attr('width', width); imageShape.attr('height', height); imageShape.attr('img', ctx.canvas); }; _proto.draw = function draw(data /* , container, shapeFactory, index */ ) { var self = this; self.set(MAPPED_DATA, data); self._prepareRange(); self._prepareSize(); var size = self.get(HEATMAP_SIZE); self._prepareGreyScaleBlurredCircle(size.radius, size.blur); var range = self.get(VALUE_RANGE); self.drawWithRange(range); // super.draw(data, container, shapeFactory, index); }; return Heatmap; }(GeomBase); GeomBase.Heatmap = Heatmap; module.exports = Heatmap;