提交 849b8038 编写于 作者: O Ovilia

feat(pie): two new alignTo layouts

上级 b6732792
...@@ -142,12 +142,25 @@ var PieSeries = echarts.extendSeriesModel({ ...@@ -142,12 +142,25 @@ var PieSeries = echarts.extendSeriesModel({
// cursor: null, // cursor: null,
left: 0,
top: 0,
right: 0,
bottom: 0,
width: null,
height: null,
label: { label: {
// If rotate around circle // If rotate around circle
rotate: false, rotate: false,
show: true, show: true,
// 'outer', 'inside', 'center' // '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%',
padding: 5,
// formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调 // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调
// 默认使用全局文本样式,详见TEXTSTYLE // 默认使用全局文本样式,详见TEXTSTYLE
// distance: 当position为inner时有效,为label位置到圆心的距离与圆半径(环状图为内外半径和)的比例系数 // distance: 当position为inner时有效,为label位置到圆心的距离与圆半径(环状图为内外半径和)的比例系数
......
...@@ -273,7 +273,7 @@ piePieceProto._updateLabel = function (data, idx, withAnimation) { ...@@ -273,7 +273,7 @@ piePieceProto._updateLabel = function (data, idx, withAnimation) {
{ {
labelFetcher: data.hostModel, labelFetcher: data.hostModel,
labelDataIndex: idx, labelDataIndex: idx,
defaultText: data.getName(idx), defaultText: labelLayout.text,
autoColor: visualColor, autoColor: visualColor,
useInsideStyle: !!labelLayout.inside useInsideStyle: !!labelLayout.inside
}, },
......
...@@ -20,16 +20,21 @@ ...@@ -20,16 +20,21 @@
// FIXME emphasis label position is not same with normal label position // FIXME emphasis label position is not same with normal label position
import * as textContain from 'zrender/src/contain/text'; import * as textContain from 'zrender/src/contain/text';
import {parsePercent} from '../../util/number';
var RADIAN = Math.PI / 180; 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) { list.sort(function (a, b) {
return a.y - b.y; return a.y - b.y;
}); });
function shiftDown(start, end, delta, dir) { function shiftDown(start, end, delta, dir) {
for (var j = start; j < end; j++) { for (var j = start; j < end; j++) {
if (list[j].y + delta > viewTop + viewHeight) {
break;
}
list[j].y += delta; list[j].y += delta;
if (j > start if (j > start
&& j + 1 < end && j + 1 < end
...@@ -45,6 +50,10 @@ function adjustSingleSide(list, cx, cy, r, dir, viewWidth, viewHeight) { ...@@ -45,6 +50,10 @@ function adjustSingleSide(list, cx, cy, r, dir, viewWidth, viewHeight) {
function shiftUp(end, delta) { function shiftUp(end, delta) {
for (var j = end; j >= 0; j--) { for (var j = end; j >= 0; j--) {
if (list[j].y - delta < viewTop) {
break;
}
list[j].y -= delta; list[j].y -= delta;
if (j > 0 if (j > 0
&& list[j].y > list[j - 1].y + list[j - 1].height && list[j].y > list[j - 1].y + list[j - 1].height
...@@ -64,6 +73,10 @@ function adjustSingleSide(list, cx, cy, r, dir, viewWidth, viewHeight) { ...@@ -64,6 +73,10 @@ function adjustSingleSide(list, cx, cy, r, dir, viewWidth, viewHeight) {
: 0; // up : 0; // up
for (var i = 0, l = list.length; i < l; i++) { 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 deltaY = Math.abs(list[i].y - cy);
var length = list[i].len; var length = list[i].len;
var length2 = list[i].len2; var length2 = list[i].len2;
...@@ -93,6 +106,12 @@ function adjustSingleSide(list, cx, cy, r, dir, viewWidth, viewHeight) { ...@@ -93,6 +106,12 @@ function adjustSingleSide(list, cx, cy, r, dir, viewWidth, viewHeight) {
var upList = []; var upList = [];
var downList = []; var downList = [];
for (var i = 0; i < len; i++) { 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; delta = list[i].y - lastY;
if (delta < 0) { if (delta < 0) {
shiftDown(i, len, -delta, dir); shiftDown(i, len, -delta, dir);
...@@ -114,40 +133,88 @@ function adjustSingleSide(list, cx, cy, r, dir, viewWidth, viewHeight) { ...@@ -114,40 +133,88 @@ function adjustSingleSide(list, cx, cy, r, dir, viewWidth, viewHeight) {
changeX(downList, true, cx, cy, r, dir); 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 leftList = [];
var rightList = []; var rightList = [];
var leftmostX = Number.MAX_VALUE;
var rightmostX = -Number.MAX_VALUE;
for (var i = 0; i < labelLayoutList.length; i++) { for (var i = 0; i < labelLayoutList.length; i++) {
if (isPositionCenter(labelLayoutList[i])) { if (isPositionCenter(labelLayoutList[i])) {
continue; continue;
} }
if (labelLayoutList[i].x < cx) { if (labelLayoutList[i].x < cx) {
leftmostX = Math.min(leftmostX, labelLayoutList[i].x);
leftList.push(labelLayoutList[i]); leftList.push(labelLayoutList[i]);
} }
else { else {
rightmostX = Math.max(rightmostX, labelLayoutList[i].x);
rightList.push(labelLayoutList[i]); rightList.push(labelLayoutList[i]);
} }
} }
adjustSingleSide(rightList, 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); adjustSingleSide(leftList, cx, cy, r, -1, viewWidth, viewHeight, viewLeft, viewTop, leftmostX);
for (var i = 0; i < labelLayoutList.length; i++) { for (var i = 0; i < labelLayoutList.length; i++) {
if (isPositionCenter(labelLayoutList[i])) { var layout = labelLayoutList[i];
if (isPositionCenter(layout)) {
continue; continue;
} }
var linePoints = labelLayoutList[i].linePoints;
var linePoints = layout.linePoints;
if (linePoints) { if (linePoints) {
var isAlignToEdge = layout.labelAlignTo === 'edge';
var realTextWidth = layout.textRect.width;
var targetTextWidth;
if (isAlignToEdge) {
// debugger;
// targetTextWidth = Math.abs(layout.x - linePoints[2][0]) - layout.labelPadding;
if (layout.x < cx) {
targetTextWidth = linePoints[2][0] - layout.labelPadding - viewLeft - layout.labelMargin;
}
else {
targetTextWidth = viewLeft + viewWidth - layout.labelMargin - linePoints[2][0] - layout.labelPadding;
}
}
else {
if (layout.x < cx) {
targetTextWidth = layout.x - viewLeft - layout.labelMargin;
}
else {
targetTextWidth = viewLeft + viewWidth - layout.x - layout.labelMargin;
}
}
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]; var dist = linePoints[1][0] - linePoints[2][0];
if (labelLayoutList[i].x < cx) { if (isAlignToEdge) {
linePoints[2][0] = labelLayoutList[i].x + 3; if (layout.x < cx) {
linePoints[2][0] = viewLeft + layout.labelMargin + realTextWidth + layout.labelPadding;
}
else {
linePoints[2][0] = viewLeft + viewWidth - layout.labelMargin
- realTextWidth - layout.labelPadding;
}
}
else {
if (layout.x < cx) {
linePoints[2][0] = layout.x + layout.labelPadding;
layout.x += layout.labelPadding;
} }
else { else {
linePoints[2][0] = labelLayoutList[i].x - 3; linePoints[2][0] = layout.x - layout.labelPadding;
layout.x -= layout.labelPadding;
} }
linePoints[1][1] = linePoints[2][1] = labelLayoutList[i].y;
linePoints[1][0] = linePoints[2][0] + dist; linePoints[1][0] = linePoints[2][0] + dist;
} }
linePoints[1][1] = linePoints[2][1] = layout.y;
}
} }
} }
...@@ -156,7 +223,7 @@ function isPositionCenter(layout) { ...@@ -156,7 +223,7 @@ function isPositionCenter(layout) {
return layout.position === 'center'; 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 data = seriesModel.getData();
var labelLayoutList = []; var labelLayoutList = [];
var cx; var cx;
...@@ -171,10 +238,16 @@ export default function (seriesModel, r, viewWidth, viewHeight, sum) { ...@@ -171,10 +238,16 @@ export default function (seriesModel, r, viewWidth, viewHeight, sum) {
var labelModel = itemModel.getModel('label'); var labelModel = itemModel.getModel('label');
// Use position in normal or emphasis // Use position in normal or emphasis
var labelPosition = labelModel.get('position') || itemModel.get('emphasis.label.position'); var labelPosition = labelModel.get('position') || itemModel.get('emphasis.label.position');
var labelPadding = labelModel.get('padding');
var labelAlignTo = labelModel.get('alignTo');
var labelMargin = parsePercent(labelModel.get('margin'), viewWidth);
var font = labelModel.getFont();
var labelLineModel = itemModel.getModel('labelLine'); var labelLineModel = itemModel.getModel('labelLine');
var labelLineLen = labelLineModel.get('length'); var labelLineLen = labelLineModel.get('length');
labelLineLen = parsePercent(labelLineLen, viewWidth);
var labelLineLen2 = labelLineModel.get('length2'); var labelLineLen2 = labelLineModel.get('length2');
labelLineLen2 = parsePercent(labelLineLen2, viewWidth);
if (layout.angle < minShowLabelRadian) { if (layout.angle < minShowLabelRadian) {
return; return;
...@@ -192,6 +265,12 @@ export default function (seriesModel, r, viewWidth, viewHeight, sum) { ...@@ -192,6 +265,12 @@ export default function (seriesModel, r, viewWidth, viewHeight, sum) {
cx = layout.cx; cx = layout.cx;
cy = layout.cy; 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'; var isLabelInside = labelPosition === 'inside' || labelPosition === 'inner';
if (labelPosition === 'center') { if (labelPosition === 'center') {
textX = layout.cx; textX = layout.cx;
...@@ -212,14 +291,23 @@ export default function (seriesModel, r, viewWidth, viewHeight, sum) { ...@@ -212,14 +291,23 @@ export default function (seriesModel, r, viewWidth, viewHeight, sum) {
var x3 = x2 + ((dx < 0 ? -1 : 1) * labelLineLen2); var x3 = x2 + ((dx < 0 ? -1 : 1) * labelLineLen2);
var y3 = y2; 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 - labelPadding : viewLeft + viewWidth - labelMargin + labelPadding;
}
else {
textX = x3 + (dx < 0 ? -labelPadding : labelPadding);
}
textY = y3; textY = y3;
linePoints = [[x1, y1], [x2, y2], [x3, 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 labelRotate;
var rotate = labelModel.get('rotate'); var rotate = labelModel.get('rotate');
...@@ -231,11 +319,7 @@ export default function (seriesModel, r, viewWidth, viewHeight, sum) { ...@@ -231,11 +319,7 @@ export default function (seriesModel, r, viewWidth, viewHeight, sum) {
? (dx < 0 ? -midAngle + Math.PI : -midAngle) ? (dx < 0 ? -midAngle + Math.PI : -midAngle)
: 0; : 0;
} }
var text = seriesModel.getFormattedLabel(idx, 'normal')
|| data.getName(idx);
var textRect = textContain.getBoundingRect(
text, font, textAlign, 'top'
);
hasLabelRotate = !!labelRotate; hasLabelRotate = !!labelRotate;
layout.label = { layout.label = {
x: textX, x: textX,
...@@ -248,7 +332,13 @@ export default function (seriesModel, r, viewWidth, viewHeight, sum) { ...@@ -248,7 +332,13 @@ export default function (seriesModel, r, viewWidth, viewHeight, sum) {
textAlign: textAlign, textAlign: textAlign,
verticalAlign: 'middle', verticalAlign: 'middle',
rotation: labelRotate, rotation: labelRotate,
inside: isLabelInside inside: isLabelInside,
labelPadding: labelPadding,
labelAlignTo: labelAlignTo,
labelMargin:labelMargin,
textRect: textRect,
text: text,
font: font
}; };
// Not layout the inside label // Not layout the inside label
...@@ -257,6 +347,6 @@ export default function (seriesModel, r, viewWidth, viewHeight, sum) { ...@@ -257,6 +347,6 @@ export default function (seriesModel, r, viewWidth, viewHeight, sum) {
} }
}); });
if (!hasLabelRotate && seriesModel.get('avoidLabelOverlap')) { if (!hasLabelRotate && seriesModel.get('avoidLabelOverlap')) {
avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight); avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight, viewLeft, viewTop);
} }
} }
\ No newline at end of file
...@@ -19,16 +19,27 @@ ...@@ -19,16 +19,27 @@
import {parsePercent, linearMap} from '../../util/number'; import {parsePercent, linearMap} from '../../util/number';
import * as layout from '../../util/layout';
import labelLayout from './labelLayout'; import labelLayout from './labelLayout';
import * as zrUtil from 'zrender/src/core/util'; import * as zrUtil from 'zrender/src/core/util';
var PI2 = Math.PI * 2; var PI2 = Math.PI * 2;
var RADIAN = Math.PI / 180; var RADIAN = Math.PI / 180;
export function getViewRect(seriesModel, api) {
return layout.getLayoutRect(
seriesModel.getBoxLayoutParams(), {
width: api.getWidth(),
height: api.getHeight()
}
);
}
export default function (seriesType, ecModel, api, payload) { export default function (seriesType, ecModel, api, payload) {
ecModel.eachSeriesByType(seriesType, function (seriesModel) { ecModel.eachSeriesByType(seriesType, function (seriesModel) {
var data = seriesModel.getData(); var data = seriesModel.getData();
var valueDim = data.mapDimension('value'); var valueDim = data.mapDimension('value');
var viewRect = getViewRect(seriesModel, api);
var center = seriesModel.get('center'); var center = seriesModel.get('center');
var radius = seriesModel.get('radius'); var radius = seriesModel.get('radius');
...@@ -40,11 +51,11 @@ export default function (seriesType, ecModel, api, payload) { ...@@ -40,11 +51,11 @@ export default function (seriesType, ecModel, api, payload) {
center = [center, center]; center = [center, center];
} }
var width = api.getWidth(); var width = parsePercent(viewRect.width, api.getWidth());
var height = api.getHeight(); var height = parsePercent(viewRect.height, api.getHeight());
var size = Math.min(width, height); var size = Math.min(width, height);
var cx = parsePercent(center[0], width); var cx = parsePercent(center[0], width) + viewRect.x;
var cy = parsePercent(center[1], height); var cy = parsePercent(center[1], height) + viewRect.y;
var r0 = parsePercent(radius[0], size / 2); var r0 = parsePercent(radius[0], size / 2);
var r = parsePercent(radius[1], size / 2); var r = parsePercent(radius[1], size / 2);
...@@ -90,7 +101,8 @@ export default function (seriesType, ecModel, api, payload) { ...@@ -90,7 +101,8 @@ export default function (seriesType, ecModel, api, payload) {
r0: r0, r0: r0,
r: roseType r: roseType
? NaN ? NaN
: r : r,
viewRect: viewRect
}); });
return; return;
} }
...@@ -123,7 +135,8 @@ export default function (seriesType, ecModel, api, payload) { ...@@ -123,7 +135,8 @@ export default function (seriesType, ecModel, api, payload) {
r0: r0, r0: r0,
r: roseType r: roseType
? linearMap(value, extent, [r0, r]) ? linearMap(value, extent, [r0, r])
: r : r,
viewRect: viewRect
}); });
currentAngle = endAngle; currentAngle = endAngle;
...@@ -161,6 +174,6 @@ export default function (seriesType, ecModel, api, payload) { ...@@ -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);
}); });
} }
\ No newline at end of file
<!DOCTYPE html>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="lib/esl.js"></script>
<script src="lib/config.js"></script>
<script src="lib/jquery.min.js"></script>
<script src="lib/facePrint.js"></script>
<script src="lib/testHelper.js"></script>
<!-- <script src="ut/lib/canteen.js"></script> -->
<script src="lib/dat.gui.min.js"></script>
<link rel="stylesheet" href="lib/reset.css" />
</head>
<body>
<style>
</style>
<div id="main-none"></div>
<div id="main0"></div>
<div id="main1"></div>
<div id="main2"></div>
<script>
require(['echarts'], function (echarts) {
var count = 20;
var data = [];
var text = '';
for (var i = 0; i < count; i++) {
text += 'XY';
data.push({
name: text + i,
value: Math.random()
});
}
var optionNone = {
series: [{
type: 'pie',
radius: '50%',
data: data,
animation: false,
labelLine: {
length2: 15
},
label: {
margin: 20,
position: 'outer'
}
}]
};
var option0 = {
series: [{
type: 'pie',
radius: '50%',
data: data,
animation: false,
labelLine: {
length2: 15
},
label: {
margin: 20,
position: 'outer',
alignTo: 'labelLine'
}
}]
};
var option1 = {
series: [{
type: 'pie',
radius: '50%',
data: data,
animation: false,
labelLine: {
length2: 15
},
label: {
margin: 20,
position: 'outer',
alignTo: 'edge'
}
}]
};
var option2 = {
series: [{
type: 'pie',
radius: '25%',
center: ['50%', '50%'],
data: data,
animation: false,
labelLine: {
length2: 15
},
label: {
position: 'outer',
alignTo: 'edge'
},
left: 0,
right: '50%',
top: 0,
bottom: '50%'
}, {
type: 'pie',
radius: '25%',
center: ['50%', '50%'],
data: data,
animation: false,
labelLine: {
length2: 15
},
label: {
margin: 20,
position: 'outer',
alignTo: 'labelLine'
},
left: '50%',
right: 0,
top: 0,
bottom: '50%'
}, {
type: 'pie',
radius: '25%',
center: ['50%', '50%'],
data: data,
animation: false,
labelLine: {
length2: 15
},
label: {
position: 'outer',
alignTo: 'edge'
},
left: 0,
right: '50%',
top: '50%',
bottom: 0
}, {
type: 'pie',
radius: '25%',
center: ['50%', '50%'],
data: data,
animation: false,
labelLine: {
length2: 15
},
label: {
margin: 20,
position: 'outer',
alignTo: 'labelLine'
},
left: '50%',
right: 0,
top: '50%',
bottom: 0
}]
};
var chartNone = testHelper.create(echarts, 'main-none', {
title: 'alignTo: "none"',
option: optionNone
});
var chart0 = testHelper.create(echarts, 'main0', {
title: 'alignTo: "labelLine"',
option: option0
});
var chart1 = testHelper.create(echarts, 'main1', {
title: 'alignTo: "edge"',
option: option1
});
var chart2 = testHelper.create(echarts, 'main2', {
title: 'Multiple charts',
option: option2
});
var gui = new dat.GUI({
});
var config = {
length2: 15,
margin: 20
};
gui
.add(config, 'length2', 0, 300)
.onChange(function (value) {
option0.series[0].labelLine.length2 = value;
option1.series[0].labelLine.length2 = value;
optionNone.series[0].labelLine.length2 = value;
chart0.setOption(option0);
chart1.setOption(option1);
chartNone.setOption(optionNone);
for (var i = 0; i < 4; ++i) {
option2.series[i].labelLine.length2 = value;
}
chart2.setOption(option2);
});
gui
.add(config, 'margin', 0, 300)
.onChange(function (value) {
option0.series[0].label.margin = value;
option1.series[0].label.margin = value;
optionNone.series[0].label.margin = value;
chart0.setOption(option0);
chart1.setOption(option1);
chartNone.setOption(optionNone);
for (var i = 0; i < 4; ++i) {
option2.series[i].label.margin = value;
}
chart2.setOption(option2);
});
});
window.onresize = function () {
if (chart0) {
chart0.resize();
chart1.resize();
chart2.resize();
}
};
</script>
</body>
</html>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册