You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by ov...@apache.org on 2020/07/29 11:09:46 UTC

[incubator-echarts] branch feat-bar-race created (now 03fee21)

This is an automated email from the ASF dual-hosted git repository.

ovilia pushed a change to branch feat-bar-race
in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git.


      at 03fee21  fix(bar-race): fix label animation #12484

This branch includes the following new commits:

     new 03fee21  fix(bar-race): fix label animation #12484

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org


[incubator-echarts] 01/01: fix(bar-race): fix label animation #12484

Posted by ov...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ovilia pushed a commit to branch feat-bar-race
in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git

commit 03fee21840cffae72e214fd2188523857fd85ade
Author: Ovilia <zw...@gmail.com>
AuthorDate: Wed Jul 29 19:08:14 2020 +0800

    fix(bar-race): fix label animation #12484
---
 src/action/changeAxisOrder.ts                    |   5 +
 src/chart/bar/BarView.ts                         | 322 +++++++++++++----------
 src/component/axis/AxisBuilder.ts                |   5 +-
 src/coord/Axis.ts                                |  10 +-
 src/coord/cartesian/defaultAxisExtentFromData.ts |   8 +-
 src/scale/Ordinal.ts                             |  33 ++-
 src/scale/Scale.ts                               |   7 +
 src/util/graphic.ts                              |  53 ++--
 8 files changed, 277 insertions(+), 166 deletions(-)

diff --git a/src/action/changeAxisOrder.ts b/src/action/changeAxisOrder.ts
index c270dae..dd74b7e 100644
--- a/src/action/changeAxisOrder.ts
+++ b/src/action/changeAxisOrder.ts
@@ -41,6 +41,11 @@ echarts.registerAction({
     ecModel.eachComponent(
         { mainType: componentType, query: payload },
         function (componentModel) {
+            // console.log('in action')
+            if (payload.sortInfo) {
+                // console.log(payload.sortInfo);
+                componentModel.axis.setCategorySortInfo(payload.sortInfo);
+            }
         }
     );
 });
diff --git a/src/chart/bar/BarView.ts b/src/chart/bar/BarView.ts
index a1d4d0d..90855b1 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} 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,25 +182,27 @@ class BarView extends ChartView {
 
         const coord = seriesModel.coordinateSystem;
         const baseAxis = coord.getBaseAxis();
-        let valueAxis: Axis2D | RadiusAxis | AngleAxis;
         let isHorizontalOrRadial: boolean;
 
+        let lastAnimator: Animator<any> = 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 coordSysClipArea = getClipArea(coord, data);
@@ -195,8 +211,6 @@ class BarView extends ChartView {
         // 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,49 +220,19 @@ 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 realtimeDuring = () => {
             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;
-                        }
-                    };
+                const el = (data.getItemGraphicEl(idx) as Rect);
+                if (el) {
+                    const shape = el.shape;
+                    return isHorizontalOrRadial ? shape.y + shape.height : shape.x + shape.width;
                 }
-            }
-            else if (axisSort) {
-                // Sort now in the first frame
-                this._updateSort(data, orderMap, baseAxis as Axis2D, api);
-            }
-        }
-
+                else {
+                    return 0;
+                }
+            };
+            that._updateSort(data, orderMap, baseAxis as Axis2D, api);
+        };
 
         data.diff(oldData)
             .add(function (dataIndex) {
@@ -288,17 +272,38 @@ 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 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);
             })
             .update(function (newIndex, oldIndex) {
                 const itemModel = data.getItemModel(newIndex);
@@ -316,13 +321,14 @@ 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);
+                    el = null;
                     return;
                 }
 
@@ -334,58 +340,7 @@ class BarView extends ChartView {
                     }
                 }
 
-                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,
@@ -393,20 +348,40 @@ 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 ECElement).disableLabelAnimation = true;
+
+                    const animator = updateRealtimeAnimation(
+                        seriesModel,
+                        axis2DModel,
+                        animationModel,
+                        el as Rect,
+                        layout as LayoutRect,
+                        data,
+                        newIndex,
+                        isHorizontalOrRadial,
+                        false
+                    );
+                    animator && (lastAnimator = animator);
+                }
+                else if (coord.type === 'cartesian2d') {
+                    updateProps(el, {shape: layout}, seriesModel, newIndex, null);
+                }
+
+                data.setItemGraphicEl(newIndex, el);
+                // Add back
+                group.add(el);
             })
             .remove(function (dataIndex) {
                 const el = oldData.getItemGraphicEl(dataIndex) as Path;
@@ -424,6 +399,10 @@ class BarView extends ChartView {
         this._backgroundEls = bgEls;
 
         this._data = data;
+
+        if (lastAnimator) {
+            lastAnimator.during(realtimeDuring);
+        }
     }
 
     private _renderLarge(seriesModel: BarSeriesModel, ecModel: GlobalModel, api: ExtensionAPI): void {
@@ -515,19 +494,34 @@ 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
+                axisId: baseAxis.index,
+                sortInfo: this._dataSort(data, orderMap)
             } as Payload;
             api.dispatchAction(action);
         }
     }
 
+    _initSort(
+        data: List<BarSeriesModel, DefaultDataVisual>,
+        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)
+            )
+        } as Payload;
+        api.dispatchAction(action);
+    }
+
     remove() {
         this._clear(this._model);
     }
@@ -547,6 +541,7 @@ class BarView extends ChartView {
             group.removeAll();
         }
         this._data = null;
+        this._isFirstFrame = true;
     }
 
     private _removeBackground(): void {
@@ -608,7 +603,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
 }
@@ -619,7 +616,7 @@ const elementCreator: {
 
     cartesian2d(
         seriesModel, data, newIndex, layout: RectLayout, isHorizontal,
-        animationModel, isUpdate, during
+        animationModel, axisModel, isUpdate, roundCap
     ) {
         const rect = new Rect({
             shape: extend({}, layout),
@@ -628,34 +625,17 @@ const elementCreator: {
 
         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
@@ -689,6 +669,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
+            ? el.animators[el.animators.length - 1]
+            : 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<BarDataItemOption>): RectLayout | SectorLayout
 }
diff --git a/src/component/axis/AxisBuilder.ts b/src/component/axis/AxisBuilder.ts
index 3430c31..733c3fb 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;
@@ -751,7 +752,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 0cfb43e..f314526 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 2b0e58e..b82391b 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<boolean> = {};
         const tarDimMap: Dictionary<boolean> = {};
-        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 a0374f4..0a566d0 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;
@@ -123,11 +128,26 @@ 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 0bfd5fc..2b90b6c 100644
--- a/src/scale/Scale.ts
+++ b/src/scale/Scale.ts
@@ -110,6 +110,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 b351969..bf93f6a 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<typeof Path.extend>[0];
 type ExtendShapeReturn = ReturnType<typeof Path.extend>;
 
+const innerLabel = makeInner<{
+    startValue: number | (string | number)[],
+    nextValue: number | (string | number)[]
+}, ZRText>();
 
 /**
  * Extend shape with parameters
@@ -544,7 +549,9 @@ function animateOrSetLabel<Props extends PathProps>(
     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 +570,23 @@ function animateOrSetLabel<Props extends PathProps>(
             }
         }
 
+        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 +598,26 @@ function animateOrSetLabel<Props extends PathProps>(
                         interpolated[i] = (rawValues as [])[i];
                     }
                     else {
-                        const value = interpolateNumber(0, (interpolateValues as number[])[i], percent);
+                        const value = interpolateNumber(
+                            (host.startValue as number[])[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 = {};


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org