This is an automated email from the ASF dual-hosted git repository. ovilia pushed a commit to branch fix-pie-align in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git
commit 070f591ffb252bcc8e6b48ae03372b835f744060 Author: Ovilia <zwl.s...@gmail.com> AuthorDate: Tue Nov 26 18:16:46 2019 +0800 feat(pie): two new alignTo layouts --- src/chart/pie/PieSeries.js | 15 ++- src/chart/pie/PieView.js | 2 +- src/chart/pie/labelLayout.js | 134 ++++++++++++++++++---- src/chart/pie/pieLayout.js | 27 +++-- test/pie-alignTo.html | 262 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 409 insertions(+), 31 deletions(-) diff --git a/src/chart/pie/PieSeries.js b/src/chart/pie/PieSeries.js index 42a44e6..6844eec 100644 --- a/src/chart/pie/PieSeries.js +++ b/src/chart/pie/PieSeries.js @@ -142,12 +142,25 @@ 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%', + padding: 5, // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调 // 默认使用全局文本样式,详见TEXTSTYLE // distance: 当position为inner时有效,为label位置到圆心的距离与圆半径(环状图为内外半径和)的比例系数 diff --git a/src/chart/pie/PieView.js b/src/chart/pie/PieView.js index 69e6903..6df30b3 100644 --- a/src/chart/pie/PieView.js +++ b/src/chart/pie/PieView.js @@ -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 }, diff --git a/src/chart/pie/labelLayout.js b/src/chart/pie/labelLayout.js index f2492fe..67f1dac 100644 --- a/src/chart/pie/labelLayout.js +++ b/src/chart/pie/labelLayout.js @@ -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 @@ -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 @@ -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; @@ -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); @@ -114,39 +133,87 @@ 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) { + // 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]; - 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.labelPadding; + } + else { + linePoints[2][0] = viewLeft + viewWidth - layout.labelMargin + - realTextWidth - layout.labelPadding; + } } else { - linePoints[2][0] = labelLayoutList[i].x - 3; + if (layout.x < cx) { + linePoints[2][0] = layout.x + layout.labelPadding; + layout.x += layout.labelPadding; + } + else { + linePoints[2][0] = layout.x - layout.labelPadding; + layout.x -= layout.labelPadding; + } + 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; } } } @@ -156,7 +223,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; @@ -171,10 +238,16 @@ 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 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 labelLineLen = labelLineModel.get('length'); + labelLineLen = parsePercent(labelLineLen, viewWidth); var labelLineLen2 = labelLineModel.get('length2'); + labelLineLen2 = parsePercent(labelLineLen2, viewWidth); if (layout.angle < minShowLabelRadian) { return; @@ -192,6 +265,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; @@ -212,14 +291,23 @@ 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 - labelPadding : viewLeft + viewWidth - labelMargin + labelPadding; + } + else { + textX = x3 + (dx < 0 ? -labelPadding : labelPadding); + } 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'); @@ -231,11 +319,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, @@ -248,7 +332,13 @@ export default function (seriesModel, r, viewWidth, viewHeight, sum) { textAlign: textAlign, verticalAlign: 'middle', rotation: labelRotate, - inside: isLabelInside + inside: isLabelInside, + labelPadding: labelPadding, + labelAlignTo: labelAlignTo, + labelMargin:labelMargin, + textRect: textRect, + text: text, + font: font }; // Not layout the inside label @@ -257,6 +347,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); } } \ No newline at end of file diff --git a/src/chart/pie/pieLayout.js b/src/chart/pie/pieLayout.js index e72c218..e8e25b6 100644 --- a/src/chart/pie/pieLayout.js +++ b/src/chart/pie/pieLayout.js @@ -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; +export 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'); @@ -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); @@ -90,7 +101,8 @@ export default function (seriesType, ecModel, api, payload) { r0: r0, r: roseType ? NaN - : r + : r, + viewRect: viewRect }); return; } @@ -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; @@ -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 diff --git a/test/pie-alignTo.html b/test/pie-alignTo.html new file mode 100644 index 0000000..698cc6e --- /dev/null +++ b/test/pie-alignTo.html @@ -0,0 +1,262 @@ +<!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> + --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@echarts.apache.org For additional commands, e-mail: commits-h...@echarts.apache.org