diff --git a/src/action/changeAxisOrder.ts b/src/action/changeAxisOrder.ts index c270dae599..bf63080402 100644 --- a/src/action/changeAxisOrder.ts +++ b/src/action/changeAxisOrder.ts @@ -41,6 +41,9 @@ echarts.registerAction({ ecModel.eachComponent( { mainType: componentType, query: payload }, function (componentModel) { + if (payload.sortInfo) { + componentModel.axis.setCategorySortInfo(payload.sortInfo); + } } ); }); diff --git a/src/chart/bar/BarView.ts b/src/chart/bar/BarView.ts index df189e5eb1..cd5a02b3f9 100644 --- a/src/chart/bar/BarView.ts +++ b/src/chart/bar/BarView.ts @@ -17,6 +17,10 @@ * under the License. */ +import Path, {PathProps} from 'zrender/src/graphic/Path'; +import Group from 'zrender/src/graphic/Group'; +import {extend, map, defaults, each, find} from 'zrender/src/core/util'; +import type {RectLike} from 'zrender/src/core/BoundingRect'; import { Rect, Sector, @@ -29,8 +33,6 @@ import { import { getECData } from '../../util/ecData'; import { enableHoverEmphasis, setStatesStylesFromModel } from '../../util/states'; import { setLabelStyle, getLabelStatesModels } from '../../label/labelStyle'; -import Path, { PathProps } from 'zrender/src/graphic/Path'; -import Group from 'zrender/src/graphic/Group'; import {throttle} from '../../util/throttle'; import {createClipPath} from '../helper/createClipPathFromCoordSys'; import Sausage from '../../util/shape/sausage'; @@ -45,20 +47,23 @@ import { OrdinalSortInfo, Payload, OrdinalNumber, - ParsedValue + ParsedValue, + ECElement } from '../../util/types'; -import BarSeriesModel, { BarSeriesOption, BarDataItemOption } from './BarSeries'; +import BarSeriesModel, {BarSeriesOption, BarDataItemOption} from './BarSeries'; import type Axis2D from '../../coord/cartesian/Axis2D'; import type Cartesian2D from '../../coord/cartesian/Cartesian2D'; -import type { RectLike } from 'zrender/src/core/BoundingRect'; import type Model from '../../model/Model'; import { isCoordinateSystemType } from '../../coord/CoordinateSystem'; import { getDefaultLabel } from '../helper/labelHelper'; import OrdinalScale from '../../scale/Ordinal'; import AngleAxis from '../../coord/polar/AngleAxis'; import RadiusAxis from '../../coord/polar/RadiusAxis'; -import { extend, map, defaults, each } from 'zrender/src/core/util'; import SeriesModel from '../../model/Series'; +import {AngleAxisModel, RadiusAxisModel} from '../../coord/polar/AxisModel'; +import CartesianAxisModel from '../../coord/cartesian/AxisModel'; +import {LayoutRect} from '../../util/layout'; +import Animator from 'zrender/src/animation/Animator'; const BAR_BORDER_WIDTH_QUERY = ['itemStyle', 'borderWidth'] as const; const BAR_BORDER_RADIUS_QUERY = ['itemStyle', 'borderRadius'] as const; @@ -108,12 +113,19 @@ class BarView extends ChartView { private _isLargeDraw: boolean; + private _isFirstFrame: boolean; // First frame after series added + private _backgroundGroup: Group; private _backgroundEls: (Rect | Sector)[]; private _model: BarSeriesModel; + constructor () { + super(); + this._isFirstFrame = true; + } + render(seriesModel: BarSeriesModel, ecModel: GlobalModel, api: ExtensionAPI, payload: Payload) { this._model = seriesModel; @@ -132,6 +144,8 @@ class BarView extends ChartView { else if (__DEV__) { console.warn('Only cartesian2d and polar supported for bar.'); } + + this._isFirstFrame = false; } incrementalPrepareRender(seriesModel: BarSeriesModel): void { @@ -168,35 +182,35 @@ class BarView extends ChartView { const coord = seriesModel.coordinateSystem; const baseAxis = coord.getBaseAxis(); - let valueAxis: Axis2D | RadiusAxis | AngleAxis; let isHorizontalOrRadial: boolean; + let lastAnimator: Animator = null; + if (coord.type === 'cartesian2d') { isHorizontalOrRadial = (baseAxis as Axis2D).isHorizontal(); - valueAxis = coord.getOtherAxis(baseAxis as Axis2D); } else if (coord.type === 'polar') { isHorizontalOrRadial = baseAxis.dim === 'angle'; - valueAxis = coord.getOtherAxis(baseAxis as (AngleAxis | RadiusAxis)); } const animationModel = seriesModel.isAnimationEnabled() ? seriesModel : null; - const axisAnimationModel = baseAxis.model; const axis2DModel = (baseAxis as Axis2D).model; const axisSort = coord.type === 'cartesian2d' && axis2DModel.get('sort') && axis2DModel.get('sortSeriesIndex') === seriesModel.seriesIndex; const realtimeSort = axisSort && axis2DModel.get('realtimeSort'); + if (realtimeSort && this._isFirstFrame) { + this._initSort(data, isHorizontalOrRadial, baseAxis as Axis2D, api); + return; + } - const needsClip = seriesModel.get('clip', true); + const needsClip = seriesModel.get('clip', true) || realtimeSort; const coordSysClipArea = getClipArea(coord, data); // If there is clipPath created in large mode. Remove it. group.removeClipPath(); // We don't use clipPath in normal mode because we needs a perfect animation // And don't want the label are clipped. - const labelModel = seriesModel.getModel('label'); - const roundCap = seriesModel.get('roundCap', true); const drawBackground = seriesModel.get('showBackground', true); @@ -206,50 +220,6 @@ class BarView extends ChartView { const bgEls: BarView['_backgroundEls'] = []; const oldBgEls = this._backgroundEls; - let hasDuringForOneData = false; - let getDuring: () => (() => void) = () => { - return null; - }; - if (coord.type === 'cartesian2d') { - const oldOrder = (baseAxis.scale as OrdinalScale).getCategorySortInfo(); - const orderMap = (idx: number) => { - return data.get(valueAxis.dim, idx) as number; - }; - - if (realtimeSort) { - // Sort in animation during - const isOrderChanged = this._isDataOrderChanged(data, orderMap, oldOrder); - if (isOrderChanged) { - getDuring = () => { - if (!hasDuringForOneData) { - hasDuringForOneData = true; - return () => { - const orderMap = (idx: number) => { - const el = (data.getItemGraphicEl(idx) as Rect); - if (el) { - const shape = el.shape; - return isHorizontalOrRadial ? shape.y + shape.height : shape.x + shape.width; - } - else { - return 0; - } - }; - that._updateSort(data, orderMap, baseAxis as Axis2D, api); - }; - } - else { - return () => null; - } - }; - } - } - else if (axisSort) { - // Sort now in the first frame - this._updateSort(data, orderMap, baseAxis as Axis2D, api); - } - } - - data.diff(oldData) .add(function (dataIndex) { const itemModel = data.getItemModel(dataIndex); @@ -271,13 +241,11 @@ class BarView extends ChartView { return; } + let isClipped = false; if (needsClip) { // Clip will modify the layout params. // And return a boolean to determine if the shape are fully clipped. - const isClipped = clip[coord.type](coordSysClipArea, layout); - if (isClipped) { - return; - } + isClipped = clip[coord.type](coordSysClipArea, layout); } const el = elementCreator[coord.type]( @@ -287,17 +255,41 @@ class BarView extends ChartView { layout, isHorizontalOrRadial, animationModel, + baseAxis.model, false, - getDuring(), roundCap ); - data.setItemGraphicEl(dataIndex, el); - group.add(el); updateStyle( el, data, dataIndex, itemModel, layout, seriesModel, isHorizontalOrRadial, coord.type === 'polar' ); + + if (realtimeSort) { + (el as unknown as ECElement).disableLabelAnimation = true; + + const animator = updateRealtimeAnimation( + seriesModel, + axis2DModel, + animationModel, + el as Rect, + layout as LayoutRect, + data, + dataIndex, + isHorizontalOrRadial, + false + ); + animator && (lastAnimator = animator); + } + else if (coord.type === 'cartesian2d') { + initProps(el, {shape: layout} as any, seriesModel, dataIndex); + } + + data.setItemGraphicEl(dataIndex, el); + group.add(el); + if (isClipped) { + el.hide(); + } }) .update(function (newIndex, oldIndex) { const itemModel = data.getItemModel(newIndex); @@ -315,76 +307,25 @@ class BarView extends ChartView { const bgLayout = getLayout[coord.type](data, newIndex); const shape = createBackgroundShape(isHorizontalOrRadial, bgLayout, coord); updateProps( - bgEl as Path, { shape: shape }, animationModel, newIndex + bgEl as Path, {shape: shape as RectShape}, animationModel, newIndex ); } let el = oldData.getItemGraphicEl(oldIndex) as BarPossiblePath; if (!data.hasValue(newIndex)) { group.remove(el); - return; + el = null; } + let isClipped = false; if (needsClip) { - const isClipped = clip[coord.type](coordSysClipArea, layout); + isClipped = clip[coord.type](coordSysClipArea, layout); if (isClipped) { group.remove(el); - return; } } - if (el) { - if (coord.type === 'cartesian2d' - && baseAxis.type === 'category' && (baseAxis as Axis2D).model.get('sort') - ) { - const rect = layout as RectShape; - let seriesShape; - let axisShape; - if (baseAxis.dim === 'x') { - axisShape = { - x: rect.x, - width: rect.width - }; - seriesShape = { - y: rect.y, - height: rect.height - }; - } - else { - axisShape = { - y: rect.y, - height: rect.height - }; - seriesShape = { - x: rect.x, - width: rect.width - }; - } - - if (!isReorder) { - updateProps( - el as Path, - { shape: seriesShape }, - animationModel, - newIndex, - null, - getDuring() - ); - } - updateProps(el as Path, { shape: axisShape }, axisAnimationModel, newIndex, null); - } - else { - updateProps(el as Path, { - shape: layout - }, animationModel, newIndex, null); - } - - const defaultTextGetter = (values: ParsedValue | ParsedValue[]) => { - return getDefaultLabel(seriesModel.getData(), newIndex, values); - }; - updateLabel(el, data, newIndex, labelModel, seriesModel, animationModel, defaultTextGetter); - } - else { + if (!el) { el = elementCreator[coord.type]( seriesModel, data, @@ -392,20 +333,43 @@ class BarView extends ChartView { layout, isHorizontalOrRadial, animationModel, - true, - getDuring(), + baseAxis.model, + !!el, roundCap ); } - data.setItemGraphicEl(newIndex, el); - // Add back - group.add(el); - updateStyle( el, data, newIndex, itemModel, layout, seriesModel, isHorizontalOrRadial, coord.type === 'polar' ); + + if (realtimeSort) { + (el as unknown as ECElement).disableLabelAnimation = true; + + const animator = updateRealtimeAnimation( + seriesModel, + axis2DModel, + animationModel, + el as Rect, + layout as LayoutRect, + data, + newIndex, + isHorizontalOrRadial, + true + ); + animator && (lastAnimator = animator); + } + else if (coord.type === 'cartesian2d') { + updateProps(el, {shape: layout}, seriesModel, newIndex, null); + } + + data.setItemGraphicEl(newIndex, el); + // Add back + if (isClipped) { + el.hide(); + } + group.add(el); }) .remove(function (dataIndex) { const el = oldData.getItemGraphicEl(dataIndex) as Path; @@ -423,6 +387,23 @@ class BarView extends ChartView { this._backgroundEls = bgEls; this._data = data; + + if (lastAnimator) { + lastAnimator.during(percent => { + const orderMap = (idx: number) => { + const el = (data.getItemGraphicEl(idx) as Rect); + if (el) { + const shape = el.shape; + // If data is NaN, shape.xxx may be NaN, so use || 0 here in case + return (isHorizontalOrRadial ? shape.y + shape.height : shape.x + shape.width) || 0; + } + else { + return 0; + } + }; + that._updateSort(data, orderMap, baseAxis as Axis2D, api); + }); + } } private _renderLarge(seriesModel: BarSeriesModel, ecModel: GlobalModel, api: ExtensionAPI): void { @@ -514,19 +495,46 @@ class BarView extends ChartView { const oldOrder = (baseAxis.scale as OrdinalScale).getCategorySortInfo(); const isOrderChanged = this._isDataOrderChanged(data, orderMap, oldOrder); if (isOrderChanged) { - // re-sort and update in axis - const sortInfo = this._dataSort(data, orderMap); - baseAxis.setCategorySortInfo(sortInfo); - - const action = { - type: 'changeAxisOrder', - componentType: baseAxis.dim + 'Axis', - axisId: baseAxis.index - } as Payload; - api.dispatchAction(action); + const newOrder = this._dataSort(data, orderMap); + const extent = baseAxis.scale.getExtent(); + for (let i = extent[0]; i < extent[1]; ++i) { + /** + * Consider the case when A and B changed order, whose representing + * bars are both out of sight, we don't wish to trigger reorder action + * as long as the order in the view doesn't change. + */ + if (oldOrder[i].ordinalNumber !== newOrder[i].ordinalNumber) { + const action = { + type: 'changeAxisOrder', + componentType: baseAxis.dim + 'Axis', + axisId: baseAxis.index, + sortInfo: newOrder + } as Payload; + api.dispatchAction(action); + break; + } + } } } + _initSort( + data: List, + isHorizontal: boolean, + baseAxis: Axis2D, + api: ExtensionAPI + ) { + const action = { + type: 'changeAxisOrder', + componentType: baseAxis.dim + 'Axis', + axisId: baseAxis.index, + sortInfo: this._dataSort( + data, + idx => parseInt(data.get(isHorizontal ? 'y' : 'x', idx) as string, 10) || 0 + ) + } as Payload; + api.dispatchAction(action); + } + remove() { this._clear(this._model); } @@ -546,6 +554,7 @@ class BarView extends ChartView { group.removeAll(); } this._data = null; + this._isFirstFrame = true; } private _removeBackground(): void { @@ -594,6 +603,8 @@ const clip: { layout.y += layout.height; layout.height = -layout.height; } + layout.width = Math.max(layout.width, 0); + layout.height = Math.max(layout.height, 0); return clipped; }, @@ -607,7 +618,9 @@ interface ElementCreator { ( seriesModel: BarSeriesModel, data: List, newIndex: number, layout: RectLayout | SectorLayout, isHorizontalOrRadial: boolean, - animationModel: BarSeriesModel, isUpdate: boolean, during: () => void, + animationModel: BarSeriesModel, + axisModel: CartesianAxisModel | AngleAxisModel | RadiusAxisModel, + isUpdate: boolean, roundCap?: boolean ): BarPossiblePath } @@ -618,43 +631,27 @@ const elementCreator: { cartesian2d( seriesModel, data, newIndex, layout: RectLayout, isHorizontal, - animationModel, isUpdate, during + animationModel, axisModel, isUpdate, roundCap ) { const rect = new Rect({ shape: extend({}, layout), z2: 1 }); + (rect as any).__dataIndex = newIndex; rect.name = 'item'; - // Animation if (animationModel) { const rectShape = rect.shape; const animateProperty = isHorizontal ? 'height' : 'width' as 'width' | 'height'; - const animateTarget = {} as RectShape; rectShape[animateProperty] = 0; - animateTarget[animateProperty] = layout[animateProperty]; - - (isUpdate ? updateProps : initProps)(rect, { - shape: animateTarget - }, animationModel, newIndex, null, during); - - const defaultTextGetter = (values: ParsedValue | ParsedValue[]) => { - return getDefaultLabel(seriesModel.getData(), newIndex, values); - }; - - const labelModel = seriesModel.getModel('label'); - (isUpdate ? updateLabel : initLabel)( - rect, data, newIndex, labelModel, seriesModel, animationModel, defaultTextGetter - ); } - return rect; }, polar( seriesModel, data, newIndex, layout: SectorLayout, isRadial: boolean, - animationModel, isUpdate, during, roundCap + animationModel, axisModel, isUpdate, roundCap ) { // Keep the same logic with bar in catesion: use end value to control // direction. Notice that if clockwise is true (by default), the sector @@ -688,6 +685,68 @@ const elementCreator: { } }; +function updateRealtimeAnimation( + seriesModel: BarSeriesModel, + axisModel: CartesianAxisModel, + animationModel: BarSeriesModel, + el: Rect, + layout: LayoutRect, + data: List, + newIndex: number, + isHorizontal: boolean, + isUpdate: boolean +) { + // Animation + if (animationModel || axisModel) { + let seriesTarget; + let axisTarget; + if (isHorizontal) { + axisTarget = { + x: layout.x, + width: layout.width + }; + seriesTarget = { + y: layout.y, + height: layout.height + }; + } + else { + axisTarget = { + y: layout.y, + height: layout.height + }; + seriesTarget = { + x: layout.x, + width: layout.width + }; + } + + (isUpdate ? updateProps : initProps)(el, { + shape: seriesTarget + }, seriesModel, newIndex, null); + + const lastAnimator = el.animators.length + ? find(el.animators, animator => animator.targetName === 'shape' && !!animator.getTrack('width')) + : null; + + (isUpdate ? updateProps : initProps)(el, { + shape: axisTarget + }, axisModel, newIndex); + + const defaultTextGetter = (values: ParsedValue | ParsedValue[]) => { + return getDefaultLabel(seriesModel.getData(), newIndex, values); + }; + + const labelModel = seriesModel.getModel('label'); + (isUpdate ? updateLabel : initLabel)( + el, data, newIndex, labelModel, seriesModel, animationModel, defaultTextGetter + ); + + // TODO: + return lastAnimator; + } +} + interface GetLayout { (data: List, dataIndex: number, itemModel?: Model): RectLayout | SectorLayout } diff --git a/src/component/axis/AxisBuilder.ts b/src/component/axis/AxisBuilder.ts index bb755e09ae..c5f61d5011 100644 --- a/src/component/axis/AxisBuilder.ts +++ b/src/component/axis/AxisBuilder.ts @@ -32,6 +32,7 @@ import { ZRTextVerticalAlign, ZRTextAlign, ECElement, ColorString } from '../../ import { AxisBaseOption } from '../../coord/axisCommonTypes'; import Element from 'zrender/src/Element'; import { PathStyleProps } from 'zrender/src/graphic/Path'; +import OrdinalScale from '../../scale/Ordinal'; const PI = Math.PI; @@ -762,7 +763,9 @@ function buildAxisLabel( const triggerEvent = axisModel.get('triggerEvent'); each(labels, function (labelItem, index) { - const tickValue = labelItem.tickValue; + const tickValue = axis.scale.type === 'ordinal' + ? (axis.scale as OrdinalScale).getRawIndex(labelItem.tickValue) + : labelItem.tickValue; const formattedLabel = labelItem.formattedLabel; const rawLabel = labelItem.rawLabel; diff --git a/src/coord/Axis.ts b/src/coord/Axis.ts index 0cfb43e767..f314526ef1 100644 --- a/src/coord/Axis.ts +++ b/src/coord/Axis.ts @@ -177,10 +177,12 @@ class Axis { const ticksCoords = map(ticks, function (tickVal) { return { - coord: this.dataToCoord(tickVal), - tickValue: this.scale instanceof OrdinalScale - ? this.scale.getCategoryIndex(tickVal) - : tickVal + coord: this.dataToCoord( + this.scale.type === 'ordinal' + ? (this.scale as OrdinalScale).getRawIndex(tickVal) + : tickVal + ), + tickValue: tickVal }; }, this); diff --git a/src/coord/cartesian/defaultAxisExtentFromData.ts b/src/coord/cartesian/defaultAxisExtentFromData.ts index 2b0e58eecf..b82391b987 100644 --- a/src/coord/cartesian/defaultAxisExtentFromData.ts +++ b/src/coord/cartesian/defaultAxisExtentFromData.ts @@ -136,7 +136,7 @@ function calculateFilteredExtent( // For duplication removal. const condDimMap: Dictionary = {}; const tarDimMap: Dictionary = {}; - let condAxisExtent: number[]; + let condAxis: Axis; let tarAxisRecord: AxisRecord; function addCondition(axis: Axis, axisRecord: AxisRecord) { @@ -150,7 +150,7 @@ function calculateFilteredExtent( each(getDataDimensionsOnAxis(data, axis.dim), function (dataDim) { if (!hasOwn(condDimMap, dataDim)) { condDimMap[dataDim] = true; - condAxisExtent = [rawExtentResult.min, rawExtentResult.max]; + condAxis = axis; } }); } @@ -196,7 +196,7 @@ function calculateFilteredExtent( if (singleCondDim && singleTarDim) { for (let dataIdx = 0; dataIdx < dataLen; dataIdx++) { const condVal = data.get(singleCondDim, dataIdx) as number; - if (condVal >= condAxisExtent[0] && condVal <= condAxisExtent[1]) { + if (condAxis.scale.isInExtentRange(condVal)) { unionExtent(tarDimExtents[0], data.get(singleTarDim, dataIdx) as number); } } @@ -205,7 +205,7 @@ function calculateFilteredExtent( for (let dataIdx = 0; dataIdx < dataLen; dataIdx++) { for (let j = 0; j < condDimsLen; j++) { const condVal = data.get(condDims[j], dataIdx) as number; - if (condVal >= condAxisExtent[0] && condVal <= condAxisExtent[1]) { + if (condAxis.scale.isInExtentRange(condVal)) { for (let k = 0; k < tarDimsLen; k++) { unionExtent(tarDimExtents[k], data.get(tarDims[k], dataIdx) as number); } diff --git a/src/scale/Ordinal.ts b/src/scale/Ordinal.ts index a0374f4d7f..0a566d0048 100644 --- a/src/scale/Ordinal.ts +++ b/src/scale/Ordinal.ts @@ -92,7 +92,7 @@ class OrdinalScale extends Scale { while (rank <= extent[1]) { ticks.push({ - value: rank + value: this.getCategoryIndex(rank) }); rank++; } @@ -113,6 +113,11 @@ class OrdinalScale extends Scale { return this._categorySortInfo; } + /** + * Get display order after sort + * + * @param {OrdinalNumber} n index of raw data + */ getCategoryIndex(n: OrdinalNumber): OrdinalNumber { if (this._categorySortInfo.length) { return this._categorySortInfo[n].beforeSortIndex; @@ -122,12 +127,27 @@ class OrdinalScale extends Scale { } } + /** + * Get raw data index + * + * @param {OrdinalNumber} displayIndex index of display + */ + getRawIndex(displayIndex: OrdinalNumber): OrdinalNumber { + if (this._categorySortInfo.length) { + return this._categorySortInfo[displayIndex].ordinalNumber; + } + else { + return displayIndex; + } + } + /** * Get item on rank n */ getLabel(tick: ScaleTick): string { if (!this.isBlank()) { - const cateogry = this._ordinalMeta.categories[tick.value]; + const rawIndex = this.getRawIndex(tick.value); + const cateogry = this._ordinalMeta.categories[rawIndex]; // Note that if no data, ordinalMeta.categories is an empty array. // Return empty if it's not exist. return cateogry == null ? '' : cateogry + ''; @@ -142,6 +162,15 @@ class OrdinalScale extends Scale { this.unionExtent(data.getApproximateExtent(dim)); } + /** + * @override + * If value is in extent range + */ + isInExtentRange(value: number): boolean { + value = this.getCategoryIndex(value); + return this._extent[0] <= value && this._extent[1] >= value; + } + getOrdinalMeta(): OrdinalMeta { return this._ordinalMeta; } diff --git a/src/scale/Scale.ts b/src/scale/Scale.ts index 0bfd5fcfd0..2b90b6c6a9 100644 --- a/src/scale/Scale.ts +++ b/src/scale/Scale.ts @@ -109,6 +109,13 @@ abstract class Scale { } } + /** + * If value is in extent range + */ + isInExtentRange(value: number): boolean { + return this._extent[0] <= value && this._extent[1] >= value; + } + /** * When axis extent depends on data and no data exists, * axis ticks should not be drawn, which is named 'blank'. diff --git a/src/util/graphic.ts b/src/util/graphic.ts index b35196903b..b456fb6965 100644 --- a/src/util/graphic.ts +++ b/src/util/graphic.ts @@ -70,6 +70,7 @@ import List from '../data/List'; import { getLabelText } from '../label/labelStyle'; import { AnimationEasing } from 'zrender/src/animation/easing'; import { getECData } from './ecData'; +import {makeInner} from './model'; const mathMax = Math.max; @@ -80,6 +81,10 @@ const _customShapeMap: Dictionary<{ new(): Path }> = {}; type ExtendShapeOpt = Parameters[0]; type ExtendShapeReturn = ReturnType; +const innerLabel = makeInner<{ + startValue: number | (string | number)[], + nextValue: number | (string | number)[] +}, ZRText>(); /** * Extend shape with parameters @@ -459,7 +464,10 @@ export function initProps( cb?: AnimateOrSetPropsOption['cb'] | AnimateOrSetPropsOption['during'], during?: AnimateOrSetPropsOption['during'] ) { - animateOrSetProps('init', el, props, animatableModel, dataIndex, cb, during); + animateOrSetProps('init', el, props, animatableModel, dataIndex, cb, percent => { + // console.log(dataIndex, el.shape.width, percent); + during && during(percent); + }); } /** @@ -544,7 +552,9 @@ function animateOrSetLabel( const valueAnimationEnabled = labelModel && labelModel.get('valueAnimation'); if (valueAnimationEnabled) { const precisionOption = labelModel.get('precision'); - const precision: number = precisionOption === 'auto' ? 0 : precisionOption; + const precision: number = !precisionOption || precisionOption === 'auto' + ? 0 + : precisionOption; let interpolateValues: (number | string)[] | (number | string); const rawValues = seriesModel.getRawValue(dataIndex); @@ -563,10 +573,23 @@ function animateOrSetLabel( } } + const text = el.getTextContent(); + const host = text && innerLabel(text); + host && (host.startValue = host.nextValue); + const during = (percent: number) => { + const text = el.getTextContent(); + if (!text || !host) { + return; + } + let interpolated; if (isRawValueNumber) { - const value = interpolateNumber(0, interpolateValues as number, percent); + const value = interpolateNumber( + host.startValue as number || 0, + interpolateValues as number, + percent + ); interpolated = numberUtil.round(value, precision); } else { @@ -578,23 +601,27 @@ function animateOrSetLabel( interpolated[i] = (rawValues as [])[i]; } else { - const value = interpolateNumber(0, (interpolateValues as number[])[i], percent); + const startValues = host.startValue as number[]; + const value = interpolateNumber( + startValues && startValues[i] ? startValues[i] : 0, + (interpolateValues as number[])[i], + percent + ); interpolated[i] = numberUtil.round(value), precision; } } } - const text = el.getTextContent(); - if (text) { - const labelText = getLabelText({ - labelDataIndex: dataIndex, - labelFetcher: seriesModel, - defaultText: defaultTextGetter - ? defaultTextGetter(interpolated) - : interpolated + '' - }, {normal: labelModel}, interpolated); - text.style.text = labelText.normal; - text.dirty(); - } + host.nextValue = interpolated; + + const labelText = getLabelText({ + labelDataIndex: dataIndex, + labelFetcher: seriesModel, + defaultText: defaultTextGetter + ? defaultTextGetter(interpolated) + : interpolated + '' + }, {normal: labelModel}, interpolated); + text.style.text = labelText.normal; + text.dirty(); }; const props: ElementProps = {}; diff --git a/test/bar-race.html b/test/bar-race.html index 1ffcaa2d2f..e9e38413c9 100644 --- a/test/bar-race.html +++ b/test/bar-race.html @@ -52,7 +52,7 @@
- + @@ -408,7 +430,7 @@ inverse: true, axisLabel: { formatter: function (label, id) { - return label + '-' + id; + return label + 0 + id; } }, animationDuration: 300,