UNPKG

@antv/g2plot

Version:

G2 Plot, a market of plots built with the Grammar of Graphics'

360 lines 13.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var tslib_1 = require("tslib"); var g_1 = require("@antv/g"); var _ = tslib_1.__importStar(require("@antv/util")); var ANCHOR_OFFSET = 0; // 锚点偏移量 var INFLECTION_OFFSET = 15; // 拐点偏移量 var DEFAULT_COLOR = '#CCC'; var LABEL1_OFFSETY = 2; var LABEL2_OFFSETY = -2; var ADJUSTOFFSET = 15; function getEndPoint(center, angle, r) { return { x: center.x + r * Math.cos(angle), y: center.y + r * Math.sin(angle), }; } function getDefaultCfg() { return { text: { fill: 'rgba(0, 0, 0, 0.65)', fontSize: 12, }, lineWidth: 0.5, lineStroke: 'rgba(0, 0, 0, 0.45)', /** distance between label and edge */ sidePadding: 20, lineHeight: 32, }; } var SpiderLabel = /** @class */ (function () { function SpiderLabel(cfg) { this.view = cfg.view; this.fields = cfg.fields; this.formatter = cfg.formatter; this.offsetX = cfg.offsetX; this.offsetY = cfg.offsetY; this.config = _.assign(getDefaultCfg(), _.pick(cfg.style, ['lineStroke', 'lineWidth'])); if (cfg.style) { this.config.text = _.mix(this.config.text, cfg.style); } this._adjustConfig(this.config); this._init(); } SpiderLabel.prototype.draw = function () { var _this = this; if (!this.view || this.view.destroyed) { return; } /** 如果有formatter则事先处理数据 */ var data = _.clone(this.view.get('filteredData')); this.halves = [[], []]; this.container = this.view.get('frontgroundGroup').addGroup(); var shapes = this.view.get('elements')[0].getShapes(); var coord = this.view.get('coord'); var angleField = this.fields[0]; var scale = this.view.get('scales')[angleField]; var center = coord.center, startAngle = coord.startAngle; var radius = coord.getRadius(); var _a = this.view.get('panelRange'), width = _a.width, height = _a.height; this.width = width; this.height = height; var angle = startAngle; var _loop_1 = function (idx) { var d = data[idx]; // 计算每个切片的middle angle var angleValue = scale.scale(d[angleField]); var targetAngle = angle + Math.PI * 2 * angleValue; var middleAngle = angle + (targetAngle - angle) / 2; angle = targetAngle; // 根据middle angle计算锚点和拐点距离 var anchorPoint = getEndPoint(center, middleAngle, radius + ANCHOR_OFFSET); var inflectionPoint = getEndPoint(center, middleAngle, radius + INFLECTION_OFFSET); // 获取对应shape的color var color = DEFAULT_COLOR; if (this_1.fields.length === 2) { var colorField = this_1.fields[1]; var colorScale = this_1.view.get('scales')[colorField]; var colorIndex = colorScale.scale(d[colorField]); var shapeIndex = Math.floor(colorIndex * (shapes.length - 1)); color = shapes[shapeIndex].attr('fill'); } // 组装label数据 var label = { _anchor: anchorPoint, _inflection: inflectionPoint, _data: d, x: inflectionPoint.x, y: inflectionPoint.y, r: radius + INFLECTION_OFFSET, fill: color, textGroup: null, _side: null, }; // 创建label文本 var texts = []; _.each(this_1.fields, function (f) { texts.push(d[f]); }); if (this_1.formatter) { var formatted = this_1.formatter(d[angleField], { _origin: d, color: color }, idx); if (_.isString(formatted)) { formatted = [formatted]; } texts = formatted; } var textGroup = new g_1.Group(); var textAttrs = { x: 0, y: 0, fontSize: this_1.config.text.fontSize, lineHeight: this_1.config.text.fontSize, fontWeight: this_1.config.text.fontWeight, fill: this_1.config.text.fill, }; // label1:下部label var lowerText = d[angleField]; if (this_1.formatter) { lowerText = texts[0]; } var lowerTextAttrs = _.clone(textAttrs); if (texts.length === 2) { lowerTextAttrs.fontWeight = 700; } var lowerTextShape = textGroup.addShape('text', { attrs: _.mix({ textBaseline: texts.length === 2 ? 'top' : 'middle', text: lowerText, }, lowerTextAttrs), data: d, offsetY: texts.length === 2 ? LABEL1_OFFSETY : 0, name: 'label', }); lowerTextShape.name = 'label'; // 用于事件标记 shapeName /** label2:上部label */ if (texts.length === 2) { var topTextShape = textGroup.addShape('text', { attrs: _.mix({ textBaseline: 'bottom', text: texts[1], }, textAttrs), data: d, offsetY: LABEL2_OFFSETY, name: 'label', }); topTextShape.name = 'label'; // 用于事件标记 shapeName } label.textGroup = textGroup; /** 将label分组 */ if (anchorPoint.x < center.x) { label._side = 'left'; this_1.halves[0].push(label); } else { label._side = 'right'; this_1.halves[1].push(label); } }; var this_1 = this; // tslint:disable-next-line: prefer-for-of for (var idx = 0; idx < data.length; idx++) { _loop_1(idx); } /** 绘制label */ var _drawnLabels = []; var maxCountForOneSide = Math.floor(height / this.config.lineHeight); _.each(this.halves, function (half) { if (half.length > maxCountForOneSide) { half.splice(maxCountForOneSide, half.length - maxCountForOneSide); } half.sort(function (a, b) { return a.y - b.y; }); _this._antiCollision(half); }); this.view.get('canvas').draw(); }; SpiderLabel.prototype.clear = function () { if (this.container) { this.container.clear(); } }; SpiderLabel.prototype._init = function () { var _this = this; this.view.on('beforerender', function () { _this.clear(); }); this.view.on('afterrender', function () { _this.draw(); }); }; SpiderLabel.prototype._antiCollision = function (half) { var _this = this; var coord = this.view.get('coord'); var canvasHeight = coord.getHeight(); var center = coord.center; var radius = coord.getRadius(); var startY = center.y - radius - INFLECTION_OFFSET - this.config.lineHeight; var overlapping = true; var totalH = canvasHeight; var i; var maxY = 0; var minY = Number.MIN_VALUE; var maxLabelWidth = 0; var boxes = half.map(function (label) { var labelY = label.y; if (labelY > maxY) { maxY = labelY; } if (labelY < minY) { minY = labelY; } var textGroup = label.textGroup; var labelWidth = textGroup.getBBox().width; if (labelWidth >= maxLabelWidth) { maxLabelWidth = labelWidth; } return { size: _this.config.lineHeight, targets: [labelY - startY], }; }); if (maxY - startY > totalH) { totalH = maxY - startY; } var iteratorBoxed = function (items) { items.forEach(function (box) { var target = (Math.min.apply(minY, box.targets) + Math.max.apply(minY, box.targets)) / 2; box.pos = Math.min(Math.max(minY, target - box.size / 2), totalH - box.size); }); }; while (overlapping) { iteratorBoxed(boxes); // detect overlapping and join boxes overlapping = false; i = boxes.length; while (i--) { if (i > 0) { var previousBox = boxes[i - 1]; var box = boxes[i]; if (previousBox.pos + previousBox.size > box.pos) { // overlapping previousBox.size += box.size; previousBox.targets = previousBox.targets.concat(box.targets); // overflow, shift up if (previousBox.pos + previousBox.size > totalH) { previousBox.pos = totalH - previousBox.size; } boxes.splice(i, 1); // removing box overlapping = true; } } } } i = 0; boxes.forEach(function (b) { var posInCompositeBox = startY; // middle of the label b.targets.forEach(function () { half[i].y = b.pos + posInCompositeBox + _this.config.lineHeight / 2; posInCompositeBox += _this.config.lineHeight; i++; }); }); var drawnLabels = []; half.forEach(function (label) { var textGroup = _this._drawLabel(label); _this.container.add(textGroup); _this._drawLabelLine(label, maxLabelWidth); drawnLabels.push(textGroup); }); }; SpiderLabel.prototype._drawLabel = function (label) { var coord = this.view.get('coord'); var center = coord.getCenter(); var radius = coord.getRadius(); var width = this.width; var y = label.y, textGroup = label.textGroup; var children = textGroup.get('children'); var x_dir = label._side === 'left' ? 1 : -1; var textAttrs = { textAlign: label._side === 'left' ? 'right' : 'left', x: label._side === 'left' ? center.x - radius - this.config.sidePadding : center.x + radius + this.config.sidePadding, }; if (this.offsetX) { textAttrs.x += this.offsetX * x_dir; } children.forEach(function (child) { var offsetY = child.get('offsetY'); var yPosition = y + offsetY; child.attr(textAttrs); child.attr('y', yPosition); }); return textGroup; }; SpiderLabel.prototype._drawLabelLine = function (label, maxLabelWidth) { var _anchor = [label._anchor.x, label._anchor.y]; var _inflection = [label._inflection.x, label._inflection.y]; var fill = label.fill, y = label.y, textGroup = label.textGroup; if (!textGroup) return; var lastPoint = [label._side === 'left' ? textGroup.getBBox().maxX + 4 : textGroup.getBBox().minX - 4, y]; var points = [_anchor, _inflection, lastPoint]; if (_inflection[1] !== y) { // 展示全部文本文本位置做过调整 if (_inflection[1] < y) { // 文本被调整下去了,则添加拐点连接线 var point1 = _inflection; var leftPoint = lastPoint[0] + maxLabelWidth + ADJUSTOFFSET; var rightPoint = lastPoint[0] - maxLabelWidth - ADJUSTOFFSET; var point2 = [label._side === 'left' ? leftPoint : rightPoint, _inflection[1]]; var point3 = [ label._side === 'left' ? lastPoint[0] + maxLabelWidth : lastPoint[0] - maxLabelWidth, lastPoint[1], ]; points = [_anchor, point1, point2, point3, lastPoint]; if ((label._side === 'right' && point2[0] < point1[0]) || (label._side === 'left' && point2[0] > point1[0])) { points = [_anchor, point3, lastPoint]; } } else { points = [_anchor, [_inflection[0], y], lastPoint]; } } var path = []; for (var i = 0; i < points.length; i++) { var p = points[i]; var starter = 'L'; if (i === 0) { starter = 'M'; } path.push([starter, p[0], p[1]]); } this.container.addShape('path', { attrs: { path: path, lineWidth: this.config.lineWidth, stroke: this.config.lineStroke, }, }); // 绘制锚点 // this.container.addShape('circle', { // attrs: { // x: _anchor[0], // y: _anchor[1], // r: this.config.anchorSize, // fill, // }, // }); }; SpiderLabel.prototype._adjustConfig = function (config) { if (config.text.fontSize) { config.lineHeight = config.text.fontSize * 3; } }; return SpiderLabel; }()); exports.default = SpiderLabel; //# sourceMappingURL=spider-label.js.map