Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(pie): two new alignTo layouts #11715

Merged
merged 10 commits into from
Nov 28, 2019
16 changes: 15 additions & 1 deletion src/chart/pie/PieSeries.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,26 @@ var PieSeries = echarts.extendSeriesModel({

// cursor: null,

left: 0,
top: 0,
right: 0,
bottom: 0,
width: null,
height: null,

label: {
// If rotate around circle
rotate: false,
show: true,
// 'outer', 'inside', 'center'
position: 'outer'
position: 'outer',
// 'none', 'labelLine', 'edge'. Works only when position is 'outer'
alignTo: 'none',
// Closest distance between label and chart edge.
// Works only position is 'outer' and alignTo is 'labelLine' or 'edge'.
margin: '25%',
bleedMargin: 10,
distanceToLabelLine: 5
// formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调
// 默认使用全局文本样式,详见TEXTSTYLE
// distance: 当position为inner时有效,为label位置到圆心的距离与圆半径(环状图为内外半径和)的比例系数
Expand Down
2 changes: 1 addition & 1 deletion src/chart/pie/PieView.js
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ piePieceProto._updateLabel = function (data, idx, withAnimation) {
{
labelFetcher: data.hostModel,
labelDataIndex: idx,
defaultText: data.getName(idx),
defaultText: labelLayout.text,
autoColor: visualColor,
useInsideStyle: !!labelLayout.inside
},
Expand Down
136 changes: 114 additions & 22 deletions src/chart/pie/labelLayout.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,21 @@
// FIXME emphasis label position is not same with normal label position

import * as textContain from 'zrender/src/contain/text';
import {parsePercent} from '../../util/number';

var RADIAN = Math.PI / 180;

function adjustSingleSide(list, cx, cy, r, dir, viewWidth, viewHeight) {
function adjustSingleSide(list, cx, cy, r, dir, viewWidth, viewHeight, viewLeft, viewTop, farthestX) {
list.sort(function (a, b) {
return a.y - b.y;
});

function shiftDown(start, end, delta, dir) {
for (var j = start; j < end; j++) {
if (list[j].y + delta > viewTop + viewHeight) {
break;
}

list[j].y += delta;
if (j > start
&& j + 1 < end
Expand All @@ -45,6 +50,10 @@ function adjustSingleSide(list, cx, cy, r, dir, viewWidth, viewHeight) {

function shiftUp(end, delta) {
for (var j = end; j >= 0; j--) {
if (list[j].y - delta < viewTop) {
break;
}

list[j].y -= delta;
if (j > 0
&& list[j].y > list[j - 1].y + list[j - 1].height
Expand All @@ -64,6 +73,10 @@ function adjustSingleSide(list, cx, cy, r, dir, viewWidth, viewHeight) {
: 0; // up

for (var i = 0, l = list.length; i < l; i++) {
if (list[i].labelAlignTo !== 'none') {
continue;
}

var deltaY = Math.abs(list[i].y - cy);
var length = list[i].len;
var length2 = list[i].len2;
Expand Down Expand Up @@ -93,6 +106,12 @@ function adjustSingleSide(list, cx, cy, r, dir, viewWidth, viewHeight) {
var upList = [];
var downList = [];
for (var i = 0; i < len; i++) {
if (list[i].position === 'outer' && list[i].labelAlignTo === 'labelLine') {
var dx = list[i].x - farthestX;
list[i].linePoints[1][0] += dx;
list[i].x = farthestX;
}

delta = list[i].y - lastY;
if (delta < 0) {
shiftDown(i, len, -delta, dir);
Expand All @@ -114,39 +133,85 @@ function adjustSingleSide(list, cx, cy, r, dir, viewWidth, viewHeight) {
changeX(downList, true, cx, cy, r, dir);
}

function avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight) {
function avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight, viewLeft, viewTop) {
var leftList = [];
var rightList = [];
var leftmostX = Number.MAX_VALUE;
var rightmostX = -Number.MAX_VALUE;
for (var i = 0; i < labelLayoutList.length; i++) {
if (isPositionCenter(labelLayoutList[i])) {
continue;
}
if (labelLayoutList[i].x < cx) {
leftmostX = Math.min(leftmostX, labelLayoutList[i].x);
leftList.push(labelLayoutList[i]);
}
else {
rightmostX = Math.max(rightmostX, labelLayoutList[i].x);
rightList.push(labelLayoutList[i]);
}
}

adjustSingleSide(rightList, cx, cy, r, 1, viewWidth, viewHeight);
adjustSingleSide(leftList, cx, cy, r, -1, viewWidth, viewHeight);
adjustSingleSide(rightList, cx, cy, r, 1, viewWidth, viewHeight, viewLeft, viewTop, rightmostX);
adjustSingleSide(leftList, cx, cy, r, -1, viewWidth, viewHeight, viewLeft, viewTop, leftmostX);

for (var i = 0; i < labelLayoutList.length; i++) {
if (isPositionCenter(labelLayoutList[i])) {
var layout = labelLayoutList[i];
if (isPositionCenter(layout)) {
continue;
}
var linePoints = labelLayoutList[i].linePoints;

var linePoints = layout.linePoints;
if (linePoints) {
var isAlignToEdge = layout.labelAlignTo === 'edge';

var realTextWidth = layout.textRect.width;
var targetTextWidth;
if (isAlignToEdge) {
if (layout.x < cx) {
targetTextWidth = linePoints[2][0] - layout.labelDistance
- viewLeft - layout.labelMargin;
}
else {
targetTextWidth = viewLeft + viewWidth - layout.labelMargin
- linePoints[2][0] - layout.labelDistance;
}
}
else {
if (layout.x < cx) {
targetTextWidth = layout.x - viewLeft - layout.bleedMargin;
}
else {
targetTextWidth = viewLeft + viewWidth - layout.x - layout.bleedMargin;
}
}
if (targetTextWidth < layout.textRect.width) {
layout.text = textContain.truncateText(layout.text, targetTextWidth, layout.font);
if (layout.labelAlignTo === 'edge') {
realTextWidth = textContain.getWidth(layout.text, layout.font);
}
}

var dist = linePoints[1][0] - linePoints[2][0];
if (labelLayoutList[i].x < cx) {
linePoints[2][0] = labelLayoutList[i].x + 3;
if (isAlignToEdge) {
if (layout.x < cx) {
linePoints[2][0] = viewLeft + layout.labelMargin + realTextWidth + layout.labelDistance;
}
else {
linePoints[2][0] = viewLeft + viewWidth - layout.labelMargin
- realTextWidth - layout.labelDistance;
}
}
else {
linePoints[2][0] = labelLayoutList[i].x - 3;
if (layout.x < cx) {
linePoints[2][0] = layout.x + layout.labelDistance;
}
else {
linePoints[2][0] = layout.x - layout.labelDistance;
}
linePoints[1][0] = linePoints[2][0] + dist;
}
linePoints[1][1] = linePoints[2][1] = labelLayoutList[i].y;
linePoints[1][0] = linePoints[2][0] + dist;
linePoints[1][1] = linePoints[2][1] = layout.y;
}
}
}
Expand All @@ -156,7 +221,7 @@ function isPositionCenter(layout) {
return layout.position === 'center';
}

export default function (seriesModel, r, viewWidth, viewHeight, sum) {
export default function (seriesModel, r, viewWidth, viewHeight, viewLeft, viewTop) {
var data = seriesModel.getData();
var labelLayoutList = [];
var cx;
Expand All @@ -171,10 +236,17 @@ export default function (seriesModel, r, viewWidth, viewHeight, sum) {
var labelModel = itemModel.getModel('label');
// Use position in normal or emphasis
var labelPosition = labelModel.get('position') || itemModel.get('emphasis.label.position');
var labelDistance = labelModel.get('distanceToLabelLine');
var labelAlignTo = labelModel.get('alignTo');
var labelMargin = parsePercent(labelModel.get('margin'), viewWidth);
var bleedMargin = labelModel.get('bleedMargin');
var font = labelModel.getFont();

var labelLineModel = itemModel.getModel('labelLine');
var labelLineLen = labelLineModel.get('length');
labelLineLen = parsePercent(labelLineLen, viewWidth);
var labelLineLen2 = labelLineModel.get('length2');
labelLineLen2 = parsePercent(labelLineLen2, viewWidth);

if (layout.angle < minShowLabelRadian) {
return;
Expand All @@ -192,6 +264,12 @@ export default function (seriesModel, r, viewWidth, viewHeight, sum) {
cx = layout.cx;
cy = layout.cy;

var text = seriesModel.getFormattedLabel(idx, 'normal')
|| data.getName(idx);
var textRect = textContain.getBoundingRect(
text, font, textAlign, 'top'
);

var isLabelInside = labelPosition === 'inside' || labelPosition === 'inner';
if (labelPosition === 'center') {
textX = layout.cx;
Expand All @@ -212,14 +290,25 @@ export default function (seriesModel, r, viewWidth, viewHeight, sum) {
var x3 = x2 + ((dx < 0 ? -1 : 1) * labelLineLen2);
var y3 = y2;

textX = x3 + (dx < 0 ? -5 : 5);
if (labelAlignTo === 'edge') {
// Adjust textX because text align of edge is opposite
textX = dx < 0
? viewLeft + labelMargin
: viewLeft + viewWidth - labelMargin;
}
else {
textX = x3 + (dx < 0 ? -labelDistance : labelDistance);
}
textY = y3;
linePoints = [[x1, y1], [x2, y2], [x3, y3]];
}

textAlign = isLabelInside ? 'center' : (dx > 0 ? 'left' : 'right');
textAlign = isLabelInside
? 'center'
: (labelAlignTo === 'edge'
? (dx > 0 ? 'right' : 'left')
: (dx > 0 ? 'left' : 'right'));
}
var font = labelModel.getFont();

var labelRotate;
var rotate = labelModel.get('rotate');
Expand All @@ -231,11 +320,7 @@ export default function (seriesModel, r, viewWidth, viewHeight, sum) {
? (dx < 0 ? -midAngle + Math.PI : -midAngle)
: 0;
}
var text = seriesModel.getFormattedLabel(idx, 'normal')
|| data.getName(idx);
var textRect = textContain.getBoundingRect(
text, font, textAlign, 'top'
);

hasLabelRotate = !!labelRotate;
layout.label = {
x: textX,
Expand All @@ -248,7 +333,14 @@ export default function (seriesModel, r, viewWidth, viewHeight, sum) {
textAlign: textAlign,
verticalAlign: 'middle',
rotation: labelRotate,
inside: isLabelInside
inside: isLabelInside,
labelDistance: labelDistance,
labelAlignTo: labelAlignTo,
labelMargin:labelMargin,
bleedMargin: bleedMargin,
textRect: textRect,
text: text,
font: font
};

// Not layout the inside label
Expand All @@ -257,6 +349,6 @@ export default function (seriesModel, r, viewWidth, viewHeight, sum) {
}
});
if (!hasLabelRotate && seriesModel.get('avoidLabelOverlap')) {
avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight);
avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight, viewLeft, viewTop);
}
}
27 changes: 20 additions & 7 deletions src/chart/pie/pieLayout.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,27 @@


import {parsePercent, linearMap} from '../../util/number';
import * as layout from '../../util/layout';
import labelLayout from './labelLayout';
import * as zrUtil from 'zrender/src/core/util';

var PI2 = Math.PI * 2;
var RADIAN = Math.PI / 180;

function getViewRect(seriesModel, api) {
return layout.getLayoutRect(
seriesModel.getBoxLayoutParams(), {
width: api.getWidth(),
height: api.getHeight()
}
);
}

export default function (seriesType, ecModel, api, payload) {
ecModel.eachSeriesByType(seriesType, function (seriesModel) {
var data = seriesModel.getData();
var valueDim = data.mapDimension('value');
var viewRect = getViewRect(seriesModel, api);

var center = seriesModel.get('center');
var radius = seriesModel.get('radius');
Expand All @@ -40,11 +51,11 @@ export default function (seriesType, ecModel, api, payload) {
center = [center, center];
}

var width = api.getWidth();
var height = api.getHeight();
var width = parsePercent(viewRect.width, api.getWidth());
var height = parsePercent(viewRect.height, api.getHeight());
var size = Math.min(width, height);
var cx = parsePercent(center[0], width);
var cy = parsePercent(center[1], height);
var cx = parsePercent(center[0], width) + viewRect.x;
var cy = parsePercent(center[1], height) + viewRect.y;
var r0 = parsePercent(radius[0], size / 2);
var r = parsePercent(radius[1], size / 2);

Expand Down Expand Up @@ -90,7 +101,8 @@ export default function (seriesType, ecModel, api, payload) {
r0: r0,
r: roseType
? NaN
: r
: r,
viewRect: viewRect
});
return;
}
Expand Down Expand Up @@ -123,7 +135,8 @@ export default function (seriesType, ecModel, api, payload) {
r0: r0,
r: roseType
? linearMap(value, extent, [r0, r])
: r
: r,
viewRect: viewRect
});

currentAngle = endAngle;
Expand Down Expand Up @@ -161,6 +174,6 @@ export default function (seriesType, ecModel, api, payload) {
}
}

labelLayout(seriesModel, r, width, height);
labelLayout(seriesModel, r, viewRect.width, viewRect.height, viewRect.x, viewRect.y);
});
}
Loading