@antv/g2plot
Version:
G2 Plot, a market of plots built with the Grammar of Graphics'
256 lines • 12.1 kB
JavaScript
;
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