提交 dae5a2b4 编写于 作者: P pissang

feat: add labelLine for all series

上级 fead6883
......@@ -28,7 +28,7 @@ import {
BoxLayoutOptionMixin,
HorizontalAlign,
LabelOption,
LabelGuideLineOption,
LabelLineOption,
ItemStyleOption,
OptionDataValueNumeric
} from '../../util/types';
......@@ -51,12 +51,12 @@ export interface FunnelDataItemOption {
height?: number | string
}
label?: FunnelLabelOption
labelLine?: LabelGuideLineOption
labelLine?: LabelLineOption
emphasis?: {
itemStyle?: ItemStyleOption
label?: FunnelLabelOption
labelLine?: LabelGuideLineOption
labelLine?: LabelLineOption
}
}
......@@ -80,12 +80,12 @@ export interface FunnelSeriesOption
funnelAlign?: HorizontalAlign
label?: FunnelLabelOption
labelLine?: LabelGuideLineOption
labelLine?: LabelLineOption
itemStyle?: ItemStyleOption
emphasis?: {
label?: FunnelLabelOption
labelLine?: LabelGuideLineOption
labelLine?: LabelLineOption
itemStyle?: ItemStyleOption
}
......
......@@ -25,30 +25,30 @@ import ExtensionAPI from '../../ExtensionAPI';
import List from '../../data/List';
import { ColorString, LabelOption } from '../../util/types';
import Model from '../../model/Model';
import { setLabelLineStyle } from '../../label/labelGuideHelper';
const opacityAccessPath = ['itemStyle', 'opacity'] as const;
/**
* Piece of pie including Sector, Label, LabelLine
*/
class FunnelPiece extends graphic.Group {
class FunnelPiece extends graphic.Polygon {
constructor(data: List, idx: number) {
super();
const polygon = new graphic.Polygon();
const polygon = this;
const labelLine = new graphic.Polyline();
const text = new graphic.Text();
this.add(polygon);
this.add(labelLine);
polygon.setTextContent(text);
this.setTextGuideLine(labelLine);
this.updateData(data, idx, true);
}
updateData(data: List, idx: number, firstCreate?: boolean) {
const polygon = this.childAt(0) as graphic.Polygon;
const polygon = this;
const seriesModel = data.hostModel;
const itemModel = data.getItemModel<FunnelDataItemOption>(idx);
......@@ -92,8 +92,8 @@ class FunnelPiece extends graphic.Group {
}
_updateLabel(data: List, idx: number) {
const polygon = this.childAt(0);
const labelLine = this.childAt(1) as graphic.Polyline;
const polygon = this;
const labelLine = this.getTextGuideLine();
const labelText = polygon.getTextContent();
const seriesModel = data.hostModel;
......@@ -131,11 +131,9 @@ class FunnelPiece extends graphic.Group {
outsideFill: visualColor
});
graphic.updateProps(labelLine, {
shape: {
labelLine.setShape({
points: labelLayout.linePoints || labelLayout.linePoints
}
}, seriesModel, idx);
});
// Make sure update style on labelText after setLabelStyle.
// Because setLabelStyle will replace a new style on it.
......@@ -153,18 +151,15 @@ class FunnelPiece extends graphic.Group {
z2: 10
});
labelLine.ignore = !labelLineModel.get('show');
const labelLineEmphasisState = labelLine.ensureState('emphasis');
labelLineEmphasisState.ignore = !labelLineHoverModel.get('show');
setLabelLineStyle(polygon, {
normal: labelLineModel,
emphasis: labelLineHoverModel
}, {
// Default use item visual color
labelLine.setStyle({
stroke: visualColor
}, {
autoCalculate: false
});
labelLine.setStyle(labelLineModel.getModel('lineStyle').getLineStyle());
const lineEmphasisState = labelLine.ensureState('emphasis');
lineEmphasisState.style = labelLineHoverModel.getModel('lineStyle').getLineStyle();
}
}
......@@ -174,6 +169,8 @@ class FunnelView extends ChartView {
private _data: List;
ignoreLabelLineUpdate = true;
render(seriesModel: FunnelSeriesModel, ecModel: GlobalModel, api: ExtensionAPI) {
const data = seriesModel.getData();
const oldData = this._data;
......
......@@ -30,7 +30,7 @@ import {
SeriesOption,
CallbackDataParams,
CircleLayoutOptionMixin,
LabelGuideLineOption,
LabelLineOption,
ItemStyleOption,
LabelOption,
BoxLayoutOptionMixin,
......@@ -57,12 +57,12 @@ export interface PieDataItemOption extends
itemStyle?: ItemStyleOption
label?: PieLabelOption
labelLine?: LabelGuideLineOption
labelLine?: LabelLineOption
emphasis?: {
itemStyle?: ItemStyleOption
label?: PieLabelOption
labelLine?: LabelGuideLineOption
labelLine?: LabelLineOption
}
}
export interface PieSeriesOption extends
......@@ -81,7 +81,7 @@ export interface PieSeriesOption extends
// TODO: TYPE Color Callback
itemStyle?: ItemStyleOption
label?: PieLabelOption
labelLine?: LabelGuideLineOption
labelLine?: LabelLineOption
clockwise?: boolean
startAngle?: number
......@@ -99,7 +99,7 @@ export interface PieSeriesOption extends
emphasis?: {
itemStyle?: ItemStyleOption
label?: PieLabelOption
labelLine?: LabelGuideLineOption
labelLine?: LabelLineOption
}
animationType?: 'expansion' | 'scale'
......
......@@ -28,6 +28,7 @@ import { Payload, ColorString, ECElement } from '../../util/types';
import List from '../../data/List';
import PieSeriesModel, {PieDataItemOption} from './PieSeries';
import labelLayout from './labelLayout';
import { setLabelLineStyle } from '../../label/labelGuideHelper';
function updateDataSelected(
this: PiePiece,
......@@ -160,13 +161,11 @@ class PiePiece extends graphic.Sector {
private _updateLabel(seriesModel: PieSeriesModel, data: List, idx: number): void {
const sector = this;
const labelLine = sector.getTextGuideLine();
const labelText = sector.getTextContent();
const itemModel = data.getItemModel<PieDataItemOption>(idx);
const labelTextEmphasisState = labelText.ensureState('emphasis');
const labelLineEmphasisState = labelLine.ensureState('emphasis');
const labelModel = itemModel.getModel('label');
const labelHoverModel = itemModel.getModel(['emphasis', 'label']);
......@@ -209,25 +208,15 @@ class PiePiece extends graphic.Sector {
labelText.ignore = !labelModel.get('show');
labelTextEmphasisState.ignore = !labelHoverModel.get('show');
labelLine.ignore = !labelLineModel.get('show');
labelLineEmphasisState.ignore = !labelLineHoverModel.get('show');
// Default use item visual color
labelLine.setStyle({
setLabelLineStyle(this, {
normal: labelLineModel,
emphasis: labelLineHoverModel
}, {
stroke: visualColor,
opacity: style && style.opacity
});
labelLine.setStyle(labelLineModel.getModel('lineStyle').getLineStyle());
const lineEmphasisState = labelLine.ensureState('emphasis');
lineEmphasisState.style = labelLineHoverModel.getModel('lineStyle').getLineStyle();
let smooth = labelLineModel.get('smooth');
if (smooth && smooth === true) {
smooth = 0.3;
}
labelLine.setShape({
smooth: smooth as number
}, {
autoCalculate: false
});
}
}
......@@ -238,6 +227,8 @@ class PieView extends ChartView {
static type = 'pie';
ignoreLabelLineUpdate = true;
private _sectorGroup: graphic.Group;
private _data: List;
......
......@@ -1097,7 +1097,7 @@ class ECharts extends Eventful {
const labelManager = this._labelManager;
labelManager.updateLayoutConfig(this._api);
labelManager.layout();
labelManager.animateLabels();
labelManager.processLabelsOverall();
}
appendData(params: {
......@@ -1727,14 +1727,13 @@ class ECharts extends Eventful {
// Add labels.
labelManager.addLabelsOfSeries(chartView);
});
scheduler.unfinished = unfinished || scheduler.unfinished;
labelManager.updateLayoutConfig(api);
labelManager.layout();
labelManager.animateLabels();
labelManager.processLabelsOverall();
ecModel.eachSeries(function (seriesModel) {
const chartView = ecIns._chartsMap[seriesModel.__viewId];
......
......@@ -36,17 +36,19 @@ import {
ZRTextVerticalAlign,
LabelLayoutOption,
LabelLayoutOptionCallback,
LabelLayoutOptionCallbackParams
LabelLayoutOptionCallbackParams,
LabelLineOption
} from '../util/types';
import { parsePercent } from '../util/number';
import ChartView from '../view/Chart';
import { ElementTextConfig } from 'zrender/src/Element';
import Element, { ElementTextConfig } from 'zrender/src/Element';
import { RectLike } from 'zrender/src/core/BoundingRect';
import Transformable from 'zrender/src/core/Transformable';
import { updateLabelGuideLine } from './labelGuideHelper';
import { updateLabelLinePoints, setLabelLineStyle } from './labelGuideHelper';
import SeriesModel from '../model/Series';
import { makeInner } from '../util/model';
import { retrieve2, guid, each, keys } from 'zrender/src/core/util';
import { retrieve2, each, keys } from 'zrender/src/core/util';
import { PathStyleProps } from 'zrender/src/graphic/Path';
interface DisplayedLabelItem {
label: ZRText
......@@ -59,7 +61,7 @@ interface DisplayedLabelItem {
interface LabelLayoutDesc {
label: ZRText
labelGuide: Polyline
labelLine: Polyline
seriesModel: SeriesModel
dataIndex: number
......@@ -186,7 +188,7 @@ class LabelManager {
this._labelList.push({
label,
labelGuide: labelGuide,
labelLine: labelGuide,
seriesModel,
dataIndex,
......@@ -228,7 +230,6 @@ class LabelManager {
attachedRot: textConfig.rotation
}
});
}
addLabelsOfSeries(chartView: ChartView) {
......@@ -253,6 +254,7 @@ class LabelManager {
// Only support label being hosted on graphic elements.
const textEl = child.getTextContent();
const dataIndex = getECData(child).dataIndex;
// Can only attach the text on the element with dataIndex
if (textEl && dataIndex != null) {
this._addLabel(dataIndex, seriesModel, textEl, layoutOption);
}
......@@ -382,18 +384,18 @@ class LabelManager {
}
}
const labelGuide = labelItem.labelGuide;
const labelLine = labelItem.labelLine;
// TODO Callback to determine if this overlap should be handled?
if (overlapped
&& labelItem.layoutOption
&& (labelItem.layoutOption as LabelLayoutOption).overlap === 'hidden'
) {
label.hide();
labelGuide && labelGuide.hide();
labelLine && labelLine.hide();
}
else {
label.attr('ignore', labelItem.defaultAttr.ignore);
labelGuide && labelGuide.attr('ignore', labelItem.defaultAttr.labelGuideIgnore);
labelLine && labelLine.attr('ignore', labelItem.defaultAttr.labelGuideIgnore);
displayedLabels.push({
label,
......@@ -405,32 +407,70 @@ class LabelManager {
});
}
updateLabelGuideLine(
label,
globalRect,
label.__hostTarget,
labelItem.hostRect,
labelItem.seriesModel.getModel(['labelLine'])
);
}
}
animateLabels() {
each(this._chartViewList, function (chartView) {
/**
* Process all labels. Not only labels with layoutOption.
*/
processLabelsOverall() {
each(this._chartViewList, (chartView) => {
const seriesModel = chartView.__model;
if (!seriesModel.isAnimationEnabled()) {
return;
}
const animationEnabled = seriesModel.isAnimationEnabled();
const ignoreLabelLineUpdate = chartView.ignoreLabelLineUpdate;
chartView.group.traverse((child) => {
if (child.ignore) {
return true; // Stop traverse descendants.
}
if (!ignoreLabelLineUpdate) {
this._updateLabelLine(child, seriesModel);
}
if (animationEnabled) {
this._animateLabels(child, seriesModel);
}
});
});
}
private _updateLabelLine(el: Element, seriesModel: SeriesModel) {
// Only support label being hosted on graphic elements.
const textEl = child.getTextContent();
const guideLine = child.getTextGuideLine();
const textEl = el.getTextContent();
// Update label line style.
const ecData = getECData(el);
const dataIndex = ecData.dataIndex;
if (textEl && dataIndex != null) {
const data = seriesModel.getData(ecData.dataType);
const itemModel = data.getItemModel<{
labelLine: LabelLineOption,
emphasis: { labelLine: LabelLineOption }
}>(dataIndex);
const defaultStyle: PathStyleProps = {};
const visualStyle = data.getItemVisual(dataIndex, 'style');
const visualType = data.getVisual('drawType');
// Default to be same with main color
defaultStyle.stroke = visualStyle[visualType];
const labelLineModel = itemModel.getModel('labelLine');
setLabelLineStyle(el, {
normal: labelLineModel,
emphasis: itemModel.getModel(['emphasis', 'labelLine'])
}, defaultStyle);
updateLabelLinePoints(el, labelLineModel);
}
}
private _animateLabels(el: Element, seriesModel: SeriesModel) {
const textEl = el.getTextContent();
const guideLine = el.getTextGuideLine();
// Animate
if (textEl && !textEl.ignore && !textEl.invisible) {
const layoutStore = labelAnimationStore(textEl);
const oldLayout = layoutStore.oldLayout;
......@@ -475,8 +515,6 @@ class LabelManager {
layoutStore.oldLayout = newLayout;
}
});
});
}
}
......
......@@ -20,19 +20,24 @@
import {
Text as ZRText,
Point,
Path
Path,
Polyline
} from '../util/graphic';
import PathProxy from 'zrender/src/core/PathProxy';
import { RectLike } from 'zrender/src/core/BoundingRect';
import { normalizeRadian } from 'zrender/src/contain/util';
import { cubicProjectPoint, quadraticProjectPoint } from 'zrender/src/core/curve';
import Element from 'zrender/src/Element';
import { LabelGuideLineOption } from '../util/types';
import { extend, defaults, retrieve2 } from 'zrender/src/core/util';
import { LabelLineOption } from '../util/types';
import Model from '../model/Model';
import { invert } from 'zrender/src/core/matrix';
const PI2 = Math.PI * 2;
const CMD = PathProxy.CMD;
const STATES = ['normal', 'emphasis'] as const;
const DEFAULT_SEARCH_SPACE = ['top', 'right', 'bottom', 'left'] as const;
type CandidatePosition = typeof DEFAULT_SEARCH_SPACE[number];
......@@ -331,50 +336,58 @@ const dir2 = new Point();
* @param target
* @param targetRect
*/
export function updateLabelGuideLine(
label: ZRText,
labelRect: RectLike,
export function updateLabelLinePoints(
target: Element,
targetRect: RectLike,
labelLineModel: Model<LabelGuideLineOption>
labelLineModel: Model<LabelLineOption>
) {
if (!target) {
return;
}
const labelLine = target.getTextGuideLine();
const label = target.getTextContent();
// Needs to create text guide in each charts.
if (!labelLine) {
if (!(label && labelLine)) {
return;
}
const labelGuideConfig = target.textGuideLineConfig || {};
if (!labelGuideConfig.autoCalculate) {
return;
}
const points = [[0, 0], [0, 0], [0, 0]];
const searchSpace = labelGuideConfig.candidates || DEFAULT_SEARCH_SPACE;
const labelRect = label.getBoundingRect().clone();
labelRect.applyTransform(label.getComputedTransform());
let minDist = Infinity;
const anchorPoint = labelGuideConfig && labelGuideConfig.anchor;
const targetTransform = target.getComputedTransform();
const targetInversedTransform = invert([], targetTransform);
const len = labelLineModel.get('length2') || 0;
if (anchorPoint) {
pt2.copy(anchorPoint);
}
for (let i = 0; i < searchSpace.length; i++) {
const candidate = searchSpace[i];
getCandidateAnchor(candidate, 0, labelRect, pt0, dir);
Point.scaleAndAdd(pt1, pt0, dir, labelGuideConfig.len == null ? 15 : labelGuideConfig.len);
Point.scaleAndAdd(pt1, pt0, dir, len);
// Transform to target coord space.
pt1.transform(targetInversedTransform);
const dist = anchorPoint ? anchorPoint.distance(pt1)
: (target instanceof Path
? nearestPointOnPath(pt1, target.path, pt2)
: nearestPointOnRect(pt1, targetRect, pt2));
: nearestPointOnRect(pt1, target.getBoundingRect(), pt2));
// TODO pt2 is in the path
if (dist < minDist) {
minDist = dist;
// Transform back to global space.
pt1.transform(targetTransform);
pt2.transform(targetTransform);
pt2.toArray(points[0]);
pt1.toArray(points[1]);
pt0.toArray(points[2]);
......@@ -441,3 +454,75 @@ export function limitTurnAngle(linePoints: number[][], minTurnAngle: number) {
tmpProjPoint.toArray(linePoints[1]);
}
}
type LabelLineModel = Model<LabelLineOption>;
/**
* Create a label line if necessary and set it's style.
*/
export function setLabelLineStyle(
targetEl: Element,
statesModels: Record<typeof STATES[number], LabelLineModel>,
defaultStyle?: Polyline['style'],
defaultConfig?: Element['textGuideLineConfig']
) {
let labelLine = targetEl.getTextGuideLine();
const label = targetEl.getTextContent();
if (!label) {
// Not show label line if there is no label.
if (labelLine) {
targetEl.removeTextGuideLine();
}
return;
}
const normalModel = statesModels.normal;
const showNormal = normalModel.get('show');
const labelShowNormal = label.ignore;
for (let i = 0; i < STATES.length; i++) {
const stateName = STATES[i];
const stateModel = statesModels[stateName];
const isNormal = stateName === 'normal';
if (stateModel) {
const stateShow = stateModel.get('show');
const isLabelIgnored = isNormal
? labelShowNormal
: retrieve2(label.states && label.states[stateName].ignore, labelShowNormal);
if (isLabelIgnored // Not show when label is not shown in this state.
|| !retrieve2(stateShow, showNormal) // Use normal state by default if not set.
) {
const stateObj = isNormal ? labelLine : (labelLine && labelLine.states.normal);
if (stateObj) {
stateObj.ignore = true;
}
continue;
}
// Create labelLine if not exists
if (!labelLine) {
labelLine = new Polyline();
targetEl.setTextGuideLine(labelLine);
}
const stateObj = isNormal ? labelLine : labelLine.ensureState(stateName);
// Make sure display.
stateObj.ignore = false;
// Set smooth
let smooth = stateModel.get('smooth');
if (smooth && smooth === true) {
smooth = 0.4;
}
stateObj.shape = stateObj.shape || {};
(stateObj.shape as Polyline['shape']).smooth = smooth as number;
const styleObj = stateModel.getModel('lineStyle').getLineStyle();
isNormal ? labelLine.useStyle(styleObj) : stateObj.style = styleObj;
}
}
if (labelLine) {
defaults(labelLine.style, defaultStyle);
// Not fill.
labelLine.style.fill = null;
}
}
......@@ -799,7 +799,7 @@ export interface LineLabelOption extends Omit<LabelOption, 'distance' | 'positio
distance?: number | number[]
}
export interface LabelGuideLineOption {
export interface LabelLineOption {
show?: boolean
length?: number
length2?: number
......@@ -1132,7 +1132,7 @@ export interface SeriesOption extends
*/
seriesLayoutBy?: 'column' | 'row'
labelLine?: LabelGuideLineOption
labelLine?: LabelLineOption
/**
* Global label layout option in label layout stage.
......
......@@ -114,6 +114,12 @@ class ChartView {
readonly renderTask: SeriesTask;
/**
* Ignore label line update in global stage. Will handle it in chart itself.
* Used in pie / funnel
*/
ignoreLabelLineUpdate: boolean;
// ----------------------
// Injectable properties
// ----------------------
......
<!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> -->
<link rel="stylesheet" href="lib/reset.css" />
</head>
<body>
<style>
</style>
<div id="main0"></div>
<script>
require(['echarts'/*, 'map/js/china' */], function (echarts) {
var option;
var data = [
[[28604,77,17096869,'Australia',1990],[31163,77.4,27662440,'Canada',1990],[1516,68,1154605773,'China',1990],[13670,74.7,10582082,'Cuba',1990],[28599,75,4986705,'Finland',1990],[29476,77.1,56943299,'France',1990],[31476,75.4,78958237,'Germany',1990],[28666,78.1,254830,'Iceland',1990],[1777,57.7,870601776,'India',1990],[29550,79.1,122249285,'Japan',1990],[2076,67.9,20194354,'North Korea',1990],[12087,72,42972254,'South Korea',1990],[24021,75.4,3397534,'New Zealand',1990],[43296,76.8,4240375,'Norway',1990],[10088,70.8,38195258,'Poland',1990],[19349,69.6,147568552,'Russia',1990],[10670,67.3,53994605,'Turkey',1990],[26424,75.7,57110117,'United Kingdom',1990],[37062,75.4,252847810,'United States',1990]],
[[44056,81.8,23968973,'Australia',2015],[43294,81.7,35939927,'Canada',2015],[13334,76.9,1376048943,'China',2015],[21291,78.5,11389562,'Cuba',2015],[38923,80.8,5503457,'Finland',2015],[37599,81.9,64395345,'France',2015],[44053,81.1,80688545,'Germany',2015],[42182,82.8,329425,'Iceland',2015],[5903,66.8,1311050527,'India',2015],[36162,83.5,126573481,'Japan',2015],[1390,71.4,25155317,'North Korea',2015],[34644,80.7,50293439,'South Korea',2015],[34186,80.6,4528526,'New Zealand',2015],[64304,81.6,5210967,'Norway',2015],[24787,77.3,38611794,'Poland',2015],[23038,73.13,143456918,'Russia',2015],[19360,76.5,78665830,'Turkey',2015],[38225,81.4,64715810,'United Kingdom',2015],[53354,79.1,321773631,'United States',2015]]
];
option = {
xAxis: {},
yAxis: {},
series: [{
name: '1990',
data: data[0],
type: 'scatter',
symbolSize: function (data) {
return Math.sqrt(data[2]) / 5e2;
},
labelLayout: {
y: 20,
align: 'center',
overlap: 'hidden'
},
labelLine: {
show: true
},
label: {
show: true,
formatter: function (param) {
return param.data[3];
},
position: 'top'
}
}, {
name: '2015',
data: data[1],
type: 'scatter',
symbolSize: function (data) {
return Math.sqrt(data[2]) / 5e2;
},
labelLayout: {
y: 40,
align: 'center',
overlap: 'hidden'
},
labelLine: {
show: true
},
label: {
show: true,
formatter: function (param) {
return param.data[3];
},
position: 'top'
}
}]
};
var chart = testHelper.create(echarts, 'main0', {
title: [
'Test Case Description of main0',
'(Muliple lines and **emphasis** are supported in description)'
],
option: option
// height: 300,
// buttons: [{text: 'btn-txt', onclick: function () {}}],
// recordCanvas: true,
});
});
</script>
</body>
</html>
......@@ -68,7 +68,7 @@ under the License.
type: 'pie',
radius: '50%',
data: data,
animation: false,
labelLine: {
length2: 15
},
......@@ -84,7 +84,7 @@ under the License.
type: 'pie',
radius: '50%',
data: data,
animation: false,
labelLine: {
length2: 15
},
......@@ -101,7 +101,7 @@ under the License.
type: 'pie',
radius: '50%',
data: data,
animation: false,
labelLine: {
length2: 15
},
......@@ -119,7 +119,7 @@ under the License.
radius: '25%',
center: ['50%', '50%'],
data: data,
animation: false,
labelLine: {
length2: 15
},
......@@ -136,7 +136,7 @@ under the License.
radius: '25%',
center: ['50%', '50%'],
data: data,
animation: false,
labelLine: {
length2: 15
},
......@@ -154,7 +154,7 @@ under the License.
radius: '25%',
center: ['50%', '50%'],
data: data,
animation: false,
labelLine: {
length2: 15
},
......@@ -171,7 +171,7 @@ under the License.
radius: '25%',
center: ['50%', '50%'],
data: data,
animation: false,
labelLine: {
length2: 15
},
......@@ -251,8 +251,6 @@ under the License.
});
});
</script>
</body>
</html>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册