UNPKG

@antv/g2plot

Version:

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

256 lines 12.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var tslib_1 = require("tslib"); var g2_1 = require("@antv/g2"); var _ = tslib_1.__importStar(require("@antv/util")); var utils_1 = require("./utils"); var base_label_1 = tslib_1.__importDefault(require("./base-label")); var inner_label_1 = require("./inner-label"); // 默认label和element的偏移 16px exports.DEFAULT_OFFSET = 16; /** label text和line距离 4px */ exports.CROOK_DISTANCE = 4; /** * @desc 环绕型 躲避 label 布局(类椭圆 - 优先顺时针偏移) */ var OuterPieLabel = /** @class */ (function (_super) { tslib_1.__extends(OuterPieLabel, _super); function OuterPieLabel() { return _super !== null && _super.apply(this, arguments) || this; } OuterPieLabel.prototype.adjustPosition = function (labels, items, coord, panel) { var _this = this; this._adjustLabelPosition(labels, coord, panel); var leftHalf = _.filter(labels, function (l) { return _.find(_this.anchors, function (a) { return a.id === l.id; }).textAlign === 'right'; }); var rightHalf = _.filter(labels, function (l) { return _.find(_this.anchors, function (a) { return a.id === l.id; }).textAlign === 'left'; }); [rightHalf, leftHalf].forEach(function (half, isLeft) { _this._antiCollision(half, coord, panel, !isLeft); }); }; /** @override */ OuterPieLabel.prototype.adjustLines = function (labels, labelItems, labelLines, coord, panel) { var _this = this; _.each(labels, function (label, idx) { var labelLine = labelLines[idx]; // 由于布局调整,修改的只是shape 所以取用使用shape(labelItem is read-only) var path = _this._getLinePath(label, coord, panel); labelLine.attr('path', path); labelLine.set('visible', label.get('visible')); }); }; /** override */ OuterPieLabel.prototype.getOffsetOfLabel = function () { var labelOptions = this.getLabelOptions(); var offset = labelOptions.offset; if (_.isString(offset)) { offset = inner_label_1.percent2Number(offset); } return offset === undefined ? exports.DEFAULT_OFFSET : offset < exports.CROOK_DISTANCE * 2 ? offset / 2 : offset - exports.CROOK_DISTANCE; }; /** labels 碰撞处理(重点算法) */ OuterPieLabel.prototype._antiCollision = function (labels, coord, plotRange, isRight) { var _this = this; var labelHeight = this.getLabelHeight(); var totalR = this.anchorRadius; var center = coord.getCenter(); var totalHeight = Math.min(plotRange.height, Math.max(totalR * 2 + labelHeight * 2, labels.length * labelHeight)); var maxLabelsCount = Math.floor(totalHeight / labelHeight); // fix-bug, maxLabelsCount 之后的labels 在非 allowOverlap 不显示(避免出现尾部label展示,而前置label不展示) if (!this.get('labelOptions').allowOverlap) { labels.slice(maxLabelsCount).forEach(function (label) { label.set('visible', false); }); } labels.splice(maxLabelsCount, labels.length - maxLabelsCount); // sort by y DESC labels.sort(function (a, b) { return a.getBBox().y - b.getBBox().y; }); // adjust y position of labels to avoid overlapping var overlapping = true; var i; var maxY = center.y + totalHeight / 2; var minY = center.y - totalHeight / 2; var boxes = labels.map(function (label) { var labelBox = label.getBBox(); if (labelBox.maxY > maxY) { maxY = Math.min(plotRange.maxY, labelBox.maxY); } if (labelBox.minY < minY) { minY = Math.max(plotRange.minY, labelBox.minY); } return { text: label.attr('text'), size: labelHeight, pos: labelBox.y, targets: [], }; }); var j = 0; while (j < boxes.length) { if (j === boxes.length - 1) { boxes[j].targets[0] = maxY; } else { boxes[j].targets[0] = boxes[j + 1].pos - boxes[j + 1].size / 2; } j++; } while (overlapping) { boxes.forEach(function (box) { var target = _.last(box.targets); box.pos = Math.max(minY, Math.min(box.pos, target - box.size)); }); // 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 var target = _.last(previousBox.targets); if (previousBox.pos + previousBox.size > target) { previousBox.pos = target - previousBox.size; } boxes.splice(i, 1); // removing box overlapping = true; } else { // 换掉最后一个 previousBox.targets.splice(-1, 1, box.pos); } } } } i = 0; // step 4: normalize y and adjust x boxes.forEach(function (b) { var posInCompositeBox = labelHeight / 2; // middle of the label b.targets.forEach(function () { labels[i].attr('y', b.pos + posInCompositeBox); posInCompositeBox += labelHeight; i++; }); }); // 调整 x 位置在椭圆轨道上 var topLabels = []; var bottomLabels = []; labels.forEach(function (label) { var anchor = _this.anchors.find(function (a) { return a.id === label.id; }); if (anchor.angle >= 0 && anchor.angle <= Math.PI) { bottomLabels.push(label); } else { topLabels.push(label); } }); [topLabels, bottomLabels].forEach(function (adjustLabels, isBottom) { if (!adjustLabels.length) { return; } var ry = isBottom ? _.last(adjustLabels).getBBox().maxY - center.y : center.y - _.head(adjustLabels).getBBox().minY; ry = Math.max(totalR, ry); var offset = _this.getOffsetOfLabel(); var distance = _this.getCrookDistance(); var maxLabelWidth = Math.max.apply(0, _.map(labels, function (label) { return label.getBBox().width; })) + offset + distance; var rx = Math.max(totalR, Math.min((ry + totalR) / 2, center.x - (plotRange.minX + maxLabelWidth))); var rxPow2 = rx * rx; var ryPow2 = ry * ry; adjustLabels.forEach(function (label) { var box = label.getBBox(); var boxCenter = { x: box.minX + box.width / 2, y: box.minY + box.height / 2 }; var dyPow2 = Math.pow(boxCenter.y - center.y, 2); var anchor = _this.anchors.find(function (a) { return a.id === label.id; }); var endPoint = utils_1.getEndPoint(center, anchor.angle, coord.getRadius()); var distance_offset = (isRight ? 1 : -1) * distance * 2; if (dyPow2 > ryPow2) { console.warn('异常(一般不会出现)', label.attr('text')); label.attr('x', endPoint.x + distance_offset); } else { // (x - cx)^2 / rx ^ 2 + (y - cy)^2 / ry ^ 2 = 1 // 避免 label的 拉线 在 element 上 var xPos = center.x + (isRight ? 1 : -1) * Math.sqrt((1 - dyPow2 / ryPow2) * rxPow2); if ((center.x === endPoint.x && boxCenter.y === endPoint.y) || (center.y === endPoint.y && xPos === endPoint.x)) { xPos = endPoint.x; } else { var k1 = (center.y - endPoint.y) / (center.x - endPoint.x); var k2 = (boxCenter.y - endPoint.y) / (xPos - endPoint.x); var theta = Math.atan((k1 - k2) / (1 + k1 * k2)); if (Math.cos(theta) > 0 && (!isRight ? xPos > endPoint.x : xPos < endPoint.x)) { xPos = endPoint.x; } } label.attr('x', xPos + distance_offset); } }); }); }; // label shape position OuterPieLabel.prototype._adjustLabelPosition = function (labels, coord, panel) { var _this = this; var distance = this.getCrookDistance(); labels.forEach(function (l) { var anchor = _this.anchors.find(function (a) { return a.id === l.id; }); var isRight = anchor.textAlign === 'left'; l.attr('x', anchor.x + (isRight ? distance * 2 : -distance * 2)); l.attr('y', anchor.y); // 统一垂直居中 l.attr('textBaseline', 'middle'); }); }; /** 获取label leader-line, 默认 not smooth */ OuterPieLabel.prototype._getLinePath = function (label, coord, panel) { var labelOptions = this.getLabelOptions(); var smooth = labelOptions.line ? labelOptions.line.smooth : false; var anchor = this.anchors.find(function (a) { return a.id === label.id; }); var angle = anchor.angle; var center = coord.getCenter(); var distance = this.getCrookDistance(); var start = utils_1.getEndPoint(center, angle, coord.getRadius()); var isRight = anchor.textAlign === 'left'; var breakAt = anchor; var end = { x: label.attr('x') + (isRight ? -distance : distance), y: label.attr('y') }; var smoothPath = []; if (end.y < anchor.y) { breakAt = tslib_1.__assign(tslib_1.__assign({}, breakAt), { id: breakAt.id, y: end.y, x: end.x + (start.x - breakAt.x) }); } smoothPath = smoothPath.concat(['Q', breakAt.x, breakAt.y]).concat([end.x, end.y]); var straightPath = ['L', /** pointy break */ breakAt.x, breakAt.y].concat(['L', end.x, end.y]); var linePath = smooth ? smoothPath : straightPath; var path = ['M', start.x, start.y].concat(linePath); return path.join(','); }; OuterPieLabel.prototype.getCrookDistance = function () { var labelOptions = this.get('labelOptions'); var offset = labelOptions.offset; return offset < exports.CROOK_DISTANCE * 2 ? offset / 2 : exports.CROOK_DISTANCE; }; /** 获取label height */ OuterPieLabel.prototype.getLabelHeight = function () { var labelOptions = this.get('labelOptions'); if (!labelOptions.labelHeight) { var renderer = this.get('labelsRenderer'); var labels = renderer.get('group').get('children'); return _.head(labels) ? _.head(labels).getBBox().height : 14; } return labelOptions.labelHeight; }; // tslint:disable OuterPieLabel.prototype.getDefaultOffset = function (point) { var offset = _super.prototype.getDefaultOffset.call(this, point); return offset === undefined ? exports.DEFAULT_OFFSET : offset <= exports.CROOK_DISTANCE ? 1 : offset - exports.CROOK_DISTANCE; }; return OuterPieLabel; }(base_label_1.default)); g2_1.registerElementLabels('outer', OuterPieLabel); //# sourceMappingURL=outer-label.js.map