diff --git a/src/chart/boxplot/BoxplotView.ts b/src/chart/boxplot/BoxplotView.ts index f48cdf67b0..5e4109a1fb 100644 --- a/src/chart/boxplot/BoxplotView.ts +++ b/src/chart/boxplot/BoxplotView.ts @@ -28,6 +28,8 @@ import ExtensionAPI from '../../core/ExtensionAPI'; import SeriesData from '../../data/SeriesData'; import { BoxplotItemLayout } from './boxplotLayout'; import { saveOldStyle } from '../../animation/basicTransition'; +import { ViewRootGroup } from '../../util/types'; +import { setBoxLabels } from '../../label/labelStyle'; class BoxplotView extends ChartView { static type = 'boxplot'; @@ -52,7 +54,7 @@ class BoxplotView extends ChartView { .add(function (newIdx) { if (data.hasValue(newIdx)) { const itemLayout = data.getItemLayout(newIdx) as BoxplotItemLayout; - const symbolEl = createNormalBox(itemLayout, data, newIdx, constDim, true); + const symbolEl = createNormalBox(itemLayout, data, newIdx, constDim, group, true); data.setItemGraphicEl(newIdx, symbolEl); group.add(symbolEl); } @@ -68,11 +70,11 @@ class BoxplotView extends ChartView { const itemLayout = data.getItemLayout(newIdx) as BoxplotItemLayout; if (!symbolEl) { - symbolEl = createNormalBox(itemLayout, data, newIdx, constDim); + symbolEl = createNormalBox(itemLayout, data, newIdx, constDim, group); } else { saveOldStyle(symbolEl); - updateNormalBoxData(itemLayout, symbolEl, data, newIdx); + updateNormalBoxData(itemLayout, symbolEl, data, newIdx, group); } group.add(symbolEl); @@ -144,6 +146,7 @@ function createNormalBox( data: SeriesData, dataIndex: number, constDim: number, + group: ViewRootGroup, isInit?: boolean ) { const ends = itemLayout.ends; @@ -156,7 +159,7 @@ function createNormalBox( } }); - updateNormalBoxData(itemLayout, el, data, dataIndex, isInit); + updateNormalBoxData(itemLayout, el, data, dataIndex, group, isInit); return el; } @@ -166,6 +169,7 @@ function updateNormalBoxData( el: BoxPath, data: SeriesData, dataIndex: number, + group: ViewRootGroup, isInit?: boolean ) { const seriesModel = data.hostModel; @@ -180,11 +184,19 @@ function updateNormalBoxData( el.useStyle(data.getItemVisual(dataIndex, 'style')); el.style.strokeNoScale = true; - el.z2 = 100; const itemModel = data.getItemModel(dataIndex); const emphasisModel = itemModel.getModel('emphasis'); + const labelModel = seriesModel.get('label'); + + if (labelModel.show) { + const formattedLabels = + ((seriesModel as BoxplotSeriesModel).getRawValue(dataIndex) as number[]) + .splice(1).map((value: number) => labelModel.formatter(value).toString()); + + setBoxLabels(itemLayout.ends, formattedLabels, labelModel, group); + } setStatesStylesFromModel(el, itemModel); diff --git a/src/label/labelStyle.ts b/src/label/labelStyle.ts index 622d574bed..dc6f8923d8 100644 --- a/src/label/labelStyle.ts +++ b/src/label/labelStyle.ts @@ -30,7 +30,8 @@ import { ColorString, ZRStyleProps, AnimationOptionMixin, - InterpolatableValue + InterpolatableValue, + ViewRootGroup } from '../util/types'; import GlobalModel from '../model/Global'; import { isFunction, retrieve2, extend, keys, trim } from 'zrender/src/core/util'; @@ -38,7 +39,7 @@ import { SPECIAL_STATES, DISPLAY_STATES } from '../util/states'; import { deprecateReplaceLog } from '../util/log'; import { makeInner, interpolateRawValues } from '../util/model'; import SeriesData from '../data/SeriesData'; -import { initProps, updateProps } from '../util/graphic'; +import * as graphic from '../util/graphic'; type TextCommonParams = { /** @@ -292,6 +293,66 @@ function setLabelStyle( } export { setLabelStyle }; +/** + * Set and create specific labels for box plot. + * + * This method should only be used for this case, + * as it manually creates multiple alternating labels + * specifically for the box plot. + */ +export function setBoxLabels(boxItemPositions: number[][], formattedLabels: Array, + labelOption: any, group: ViewRootGroup) { + // get sorted and unique y positions of box items + const yPositions = boxItemPositions.map((pos: number[]) => pos[1]); + const uniqueYPositions: number[] = []; + yPositions.forEach(position => { + if (!uniqueYPositions.includes(position)) { + uniqueYPositions.push(position); + } + }); + uniqueYPositions.sort(function (a: number, b: number) { + return b - a; + }); + + boxItemPositions.sort(function (a: number[], b: number[]) { + return a[0] - b[0]; + }); + + // get alternating y positions for labels to avoid overlap + const uniqueAlternatingPositions = uniqueYPositions.map((posY: number, ind: number) => { + const matchingPositions = boxItemPositions.filter((orgPos: number[]) => orgPos[1] === posY); + const index = (ind % 2 === 0) ? 0 : matchingPositions.length - 1; + return matchingPositions[index]; + }); + + // create labels and add them to their respective positions + formattedLabels.forEach((labelText: string, ind: number) => { + if (labelOption.show === 'iqr' && (ind === 0 || ind === 4)) { + return; + } + if (labelOption.show === 'median' && (ind !== 2)) { + return; + } + if (labelOption.show === 'whiskers' && (ind !== 0 && ind !== 4)) { + return; + } + const label = new graphic.Text({ + style: { + text: labelText + }, + z2: 1000 + }); + const defaultOffset = 5; + const defaultXOffset = (ind % 2) === 0 ? (-(label.getBoundingRect().width + defaultOffset)) : defaultOffset; + const defaultYOffset = -defaultOffset; + const customOffset = labelOption.offset ? labelOption.offset : [0, 0]; + label.x = uniqueAlternatingPositions[ind][0] + defaultXOffset + customOffset[0], + label.y = uniqueAlternatingPositions[ind][1] + defaultYOffset + customOffset[1], + group.add(label); + }); +} + + export function getLabelStatesModels( itemModel: Model & Partial>>, labelName?: LabelName @@ -726,8 +787,8 @@ export function animateLabelValue( (textEl as ZRText & {percent?: number}).percent = 0; (labelInnerStore.prevValue == null - ? initProps - : updateProps + ? graphic.initProps + : graphic.updateProps )(textEl, { // percent is used to prevent animation from being aborted #15916 percent: 1 diff --git a/test/boxplot-labels.html b/test/boxplot-labels.html new file mode 100644 index 0000000000..20693d63b9 --- /dev/null +++ b/test/boxplot-labels.html @@ -0,0 +1,256 @@ + + + + + + + + + + + + + + + + + + +
+ + + +