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

shenyi pushed a commit to branch label-enhancement
in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git


The following commit(s) were added to refs/heads/label-enhancement by this push:
     new 17eb9c0  feat(state): add stateAnimation in all series. use state in 
sunburst.
17eb9c0 is described below

commit 17eb9c0bbd95c0135bacaad8f55f97cf23a74bdf
Author: pissang <[email protected]>
AuthorDate: Fri May 15 16:25:51 2020 +0800

    feat(state): add stateAnimation in all series. use state in sunburst.
---
 src/chart/gauge/GaugeView.ts         |   4 +-
 src/chart/pie/PieView.ts             |  13 +-
 src/chart/sunburst/SunburstPiece.ts  | 296 ++++++++++++++++-------------------
 src/chart/sunburst/SunburstSeries.ts |   5 +-
 src/chart/sunburst/SunburstView.ts   |   6 +-
 src/echarts.ts                       |  25 ++-
 src/model/Model.ts                   |  62 ++++----
 src/model/Series.ts                  |   7 +-
 src/model/globalDefault.ts           |   8 +-
 src/util/graphic.ts                  | 145 +++++------------
 src/util/types.ts                    |  10 ++
 src/visual/style.ts                  |   2 +-
 12 files changed, 259 insertions(+), 324 deletions(-)

diff --git a/src/chart/gauge/GaugeView.ts b/src/chart/gauge/GaugeView.ts
index 77e3c8c..db84a9b 100644
--- a/src/chart/gauge/GaugeView.ts
+++ b/src/chart/gauge/GaugeView.ts
@@ -424,7 +424,7 @@ class GaugeView extends ChartView {
                     text: data.getName(0),
                     align: 'center',
                     verticalAlign: 'middle'
-                }, {autoColor: autoColor, forceRich: true})
+                }, {autoColor: autoColor})
             }));
         }
     }
@@ -464,7 +464,7 @@ class GaugeView extends ChartView {
                     height: isNaN(height) ? null : height,
                     align: 'center',
                     verticalAlign: 'middle'
-                }, {autoColor: autoColor, forceRich: true})
+                }, {autoColor: autoColor})
             }));
         }
     }
diff --git a/src/chart/pie/PieView.ts b/src/chart/pie/PieView.ts
index 56d1ac6..aa1b0d3 100644
--- a/src/chart/pie/PieView.ts
+++ b/src/chart/pie/PieView.ts
@@ -27,8 +27,6 @@ import ExtensionAPI from '../../ExtensionAPI';
 import { Payload, ColorString } from '../../util/types';
 import List from '../../data/List';
 import PieSeriesModel, {PieDataItemOption} from './PieSeries';
-import { ElementAnimateConfig } from 'zrender/src/Element';
-import labelLayout from './labelLayout';
 
 function updateDataSelected(
     this: PiePiece,
@@ -48,13 +46,9 @@ function updateDataSelected(
         seriesId: seriesModel.id
     });
 
-    const animationCfg: ElementAnimateConfig = {
-        duration: seriesModel.get('animation') ? 200 : 0,
-        easing: 'cubicOut'
-    };
     data.each(function (idx) {
         const el = data.getItemGraphicEl(idx);
-        el.toggleState('select', seriesModel.isSelected(data.getName(idx)), 
animationCfg);
+        el.toggleState('select', seriesModel.isSelected(data.getName(idx)));
     });
 }
 
@@ -173,10 +167,7 @@ class PiePiece extends graphic.Sector {
         graphic.enableHoverEmphasis(this);
 
         // Switch after `select` state updated.
-        sector.toggleState('select', 
seriesModel.isSelected(data.getName(idx)), {
-            duration: seriesModel.get('animation') ? 200 : 0,
-            easing: 'cubicOut'
-        });
+        sector.toggleState('select', 
seriesModel.isSelected(data.getName(idx)));
     }
 
     private _updateLabel(data: List, idx: number, withAnimation: boolean): 
void {
diff --git a/src/chart/sunburst/SunburstPiece.ts 
b/src/chart/sunburst/SunburstPiece.ts
index aa44e40..0afe37a 100644
--- a/src/chart/sunburst/SunburstPiece.ts
+++ b/src/chart/sunburst/SunburstPiece.ts
@@ -19,11 +19,13 @@
 
 import * as zrUtil from 'zrender/src/core/util';
 import * as graphic from '../../util/graphic';
-import { ColorString } from '../../util/types';
 import { TreeNode } from '../../data/Tree';
 import SunburstSeriesModel, { SunburstSeriesNodeItemOption, 
SunburstSeriesOption } from './SunburstSeries';
 import GlobalModel from '../../model/Global';
 import { AllPropTypes } from 'zrender/src/core/types';
+import { PathStyleProps } from 'zrender/src/graphic/Path';
+import { ColorString } from '../../util/types';
+import Model from '../../model/Model';
 
 const NodeHighlightPolicy = {
     NONE: 'none', // not downplay others
@@ -41,7 +43,7 @@ interface DrawTreeNode extends TreeNode {
 /**
  * Sunburstce of Sunburst including Sector, Label, LabelLine
  */
-class SunburstPiece extends graphic.Group {
+class SunburstPiece extends graphic.Sector {
 
     node: TreeNode;
 
@@ -51,22 +53,20 @@ class SunburstPiece extends graphic.Group {
     constructor(node: TreeNode, seriesModel: SunburstSeriesModel, ecModel: 
GlobalModel) {
         super();
 
-        const sector = new graphic.Sector({
-            z2: DEFAULT_SECTOR_Z,
-            textConfig: {
-                inside: true
-            }
-        });
-        this.add(sector);
-        graphic.getECData(sector).seriesIndex = seriesModel.seriesIndex;
+        this.z2 = DEFAULT_SECTOR_Z;
+        this.textConfig = {
+            inside: true
+        };
+
+        graphic.getECData(this).seriesIndex = seriesModel.seriesIndex;
 
         const text = new graphic.Text({
             z2: DEFAULT_TEXT_Z,
             silent: 
node.getModel<SunburstSeriesNodeItemOption>().get(['label', 'silent'])
         });
-        sector.setTextContent(text);
+        this.setTextContent(text);
 
-        this.updateData(true, node, 'normal', seriesModel, ecModel);
+        this.updateData(true, node, seriesModel, ecModel);
 
         // Hover to change label and labelLine
         // FIXME
@@ -85,7 +85,7 @@ class SunburstPiece extends graphic.Group {
     updateData(
         firstCreate: boolean,
         node: TreeNode,
-        state: 'emphasis' | 'normal' | 'highlight' | 'downplay',
+        // state: 'emphasis' | 'normal' | 'highlight' | 'downplay',
         seriesModel?: SunburstSeriesModel,
         ecModel?: GlobalModel
     ) {
@@ -95,7 +95,7 @@ class SunburstPiece extends graphic.Group {
         seriesModel = seriesModel || this._seriesModel;
         ecModel = ecModel || this._ecModel;
 
-        const sector = this.childAt(0) as graphic.Sector;
+        const sector = this;
         graphic.getECData(sector).dataIndex = node.dataIndex;
 
         const itemModel = node.getModel<SunburstSeriesNodeItemOption>();
@@ -108,23 +108,13 @@ class SunburstPiece extends graphic.Group {
 
         // const visualColor = getNodeColor(node, seriesModel, ecModel);
         // fillDefaultColor(node, seriesModel, visualColor);
-        const normalStyle = node.getVisual('style');
-        let style;
-        if (state === 'normal') {
-            style = normalStyle;
-        }
-        else {
-            const stateStyle = itemModel.getModel([state, 'itemStyle'])
-                .getItemStyle();
-            style = zrUtil.merge(stateStyle, normalStyle);
-        }
-        // style = zrUtil.defaults(
-        //     {
-        //         lineJoin: 'bevel',
-        //         fill: style.fill || visualColor
-        //     },
-        //     style
-        // );
+        const normalStyle = node.getVisual('style') as PathStyleProps;
+        normalStyle.lineJoin = 'bevel';
+
+        zrUtil.each(['emphasis', 'highlight', 'downplay'] as const, function 
(stateName) {
+            const state = sector.ensureState(stateName);
+            state.style = itemModel.getModel([stateName, 
'itemStyle']).getItemStyle();
+        });
 
         if (firstCreate) {
             sector.setShape(sectorShape);
@@ -139,26 +129,16 @@ class SunburstPiece extends graphic.Group {
                 seriesModel,
                 node.dataIndex
             );
-            sector.useStyle(style);
-        }
-        else if (typeof style.fill === 'object' && style.fill.type
-            || typeof sector.style.fill === 'object' && sector.style.fill.type
-        ) {
-            // Disable animation for gradient since no interpolation method
-            // is supported for gradient
-            graphic.updateProps(sector, {
-                shape: sectorShape
-            }, seriesModel);
-            sector.useStyle(style);
-        }
-        else {
-            graphic.updateProps(sector, {
-                shape: sectorShape,
-                style: style
-            }, seriesModel);
         }
 
-        this._updateLabel(seriesModel, style.fill, state);
+        // Disable animation for gradient since no interpolation method
+        // is supported for gradient
+        graphic.updateProps(sector, {
+            shape: sectorShape
+        }, seriesModel);
+        sector.useStyle(normalStyle);
+
+        this._updateLabel(seriesModel);
 
         const cursorStyle = itemModel.getShallow('cursor');
         cursorStyle && sector.attr('cursor', cursorStyle);
@@ -177,13 +157,13 @@ class SunburstPiece extends graphic.Group {
         this.node.hostTree.root.eachNode(function (n: DrawTreeNode) {
             if (n.piece) {
                 if (that.node === n) {
-                    n.piece.updateData(false, n, 'emphasis');
+                    n.piece.useState('emphasis', true);
                 }
                 else if (isNodeHighlighted(n, that.node, highlightPolicy)) {
-                    n.piece.childAt(0).trigger('highlight');
+                    n.piece.useState('highlight', true);
                 }
                 else if (highlightPolicy !== NodeHighlightPolicy.NONE) {
-                    n.piece.childAt(0).trigger('downplay');
+                    n.piece.useState('downplay', true);
                 }
             }
         });
@@ -192,141 +172,139 @@ class SunburstPiece extends graphic.Group {
     onNormal() {
         this.node.hostTree.root.eachNode(function (n: DrawTreeNode) {
             if (n.piece) {
-                n.piece.updateData(false, n, 'normal');
+                n.piece.clearStates();
+                // n.piece.updateData(false, n, 'normal');
             }
         });
     }
 
     onHighlight() {
-        this.updateData(false, this.node, 'highlight');
+        this.removeState('downplay');
+        this.useState('highlight', true);
     }
 
     onDownplay() {
-        this.updateData(false, this.node, 'downplay');
+        this.removeState('highlight');
+        this.useState('downplay', true);
     }
 
     _updateLabel(
-        seriesModel: SunburstSeriesModel,
-        visualColor: ColorString,
-        state: 'emphasis' | 'normal' | 'highlight' | 'downplay'
+        seriesModel: SunburstSeriesModel
     ) {
         const itemModel = this.node.getModel<SunburstSeriesNodeItemOption>();
-        const normalModel = itemModel.getModel('label');
-        const labelModel = state === 'normal' || state === 'emphasis'
-            ? normalModel
-            : itemModel.getModel([state, 'label']);
-        const labelHoverModel = itemModel.getModel(['emphasis', 'label']);
-
-        let text = zrUtil.retrieve(
-            seriesModel.getFormattedLabel(this.node.dataIndex, state),
-            this.node.name
-        );
-        if (getLabelAttr('show') === false) {
-            text = '';
-        }
+        const normalLabelModel = itemModel.getModel('label');
 
         const layout = this.node.getLayout();
-        let labelMinAngle = labelModel.get('minAngle');
-        if (labelMinAngle == null) {
-            labelMinAngle = normalModel.get('minAngle');
-        }
-        labelMinAngle = labelMinAngle / 180 * Math.PI;
         const angle = layout.endAngle - layout.startAngle;
-        if (labelMinAngle != null && Math.abs(angle) < labelMinAngle) {
-            // Not displaying text when angle is too small
-            text = '';
-        }
-
-        const sector = this.childAt(0);
-        const label = sector.getTextContent();
-
 
         const midAngle = (layout.startAngle + layout.endAngle) / 2;
         const dx = Math.cos(midAngle);
         const dy = Math.sin(midAngle);
 
-        let r;
-        const labelPosition = getLabelAttr('position');
-        const labelPadding = getLabelAttr('distance') || 0;
-        let textAlign = getLabelAttr('align');
-        if (labelPosition === 'outside') {
-            r = layout.r + labelPadding;
-            textAlign = midAngle > Math.PI / 2 ? 'right' : 'left';
-        }
-        else {
-            if (!textAlign || textAlign === 'center') {
-                r = (layout.r + layout.r0) / 2;
-                textAlign = 'center';
+        const sector = this;
+        const label = sector.getTextContent();
+        const dataIndex = this.node.dataIndex;
+
+        zrUtil.each(['normal', 'emphasis', 'highlight', 'downplay'] as const, 
(stateName) => {
+
+            const labelStateModel = stateName === 'normal' ? 
itemModel.getModel('label')
+                : itemModel.getModel([stateName, 'label']);
+            const labelMinAngle = labelStateModel.get('minAngle') / 180 * 
Math.PI;
+            const isNormal = stateName === 'normal';
+
+            const state = isNormal ? label : label.ensureState(stateName);
+            let text = seriesModel.getFormattedLabel(dataIndex, stateName);
+            if (isNormal) {
+                text = text || this.node.name;
             }
-            else if (textAlign === 'left') {
-                r = layout.r0 + labelPadding;
-                if (midAngle > Math.PI / 2) {
-                    textAlign = 'right';
-                }
+
+            state.style = graphic.createTextStyle(labelStateModel, {
+            }, null, stateName !== 'normal', true);
+            if (text) {
+                state.style.text = text;
             }
-            else if (textAlign === 'right') {
-                r = layout.r - labelPadding;
-                if (midAngle > Math.PI / 2) {
-                    textAlign = 'left';
-                }
+
+            // Not displaying text when angle is too small
+            state.ignore = labelMinAngle != null && Math.abs(angle) < 
labelMinAngle;
+
+            const labelPosition = getLabelAttr(labelStateModel, 'position');
+
+            const sectorState = isNormal ? sector : sector.states[stateName];
+            const labelColor = sectorState.style.fill as ColorString;
+            sectorState.textConfig = {
+                inside: labelPosition !== 'outside'
+            };
+            if (labelColor) {
+                sectorState.textConfig.insideStroke = 
sectorState.textConfig.outsideFill = labelColor;
             }
-        }
 
-        graphic.setLabelStyle(
-            label, normalModel, labelHoverModel,
-            {
-                defaultText: labelModel.getShallow('show') ? text : null
+            let r;
+            const labelPadding = getLabelAttr(labelStateModel, 'distance') || 
0;
+            let textAlign = getLabelAttr(labelStateModel, 'align');
+            if (labelPosition === 'outside') {
+                r = layout.r + labelPadding;
+                textAlign = midAngle > Math.PI / 2 ? 'right' : 'left';
+            }
+            else {
+                if (!textAlign || textAlign === 'center') {
+                    r = (layout.r + layout.r0) / 2;
+                    textAlign = 'center';
+                }
+                else if (textAlign === 'left') {
+                    r = layout.r0 + labelPadding;
+                    if (midAngle > Math.PI / 2) {
+                        textAlign = 'right';
+                    }
+                }
+                else if (textAlign === 'right') {
+                    r = layout.r - labelPadding;
+                    if (midAngle > Math.PI / 2) {
+                        textAlign = 'left';
+                    }
+                }
             }
-        );
-        sector.setTextConfig({
-            inside: labelPosition !== 'outside',
-            insideStroke: visualColor,
-            // insideFill: 'auto',
-            outsideFill: visualColor
-        });
 
-        label.attr('style', {
-            text: text,
-            align: textAlign,
-            verticalAlign: getLabelAttr('verticalAlign') || 'middle',
-            opacity: getLabelAttr('opacity')
-        });
+            state.style.align = textAlign;
+            state.style.verticalAlign = getLabelAttr(labelStateModel, 
'verticalAlign') || 'middle';
 
-        label.x = r * dx + layout.cx;
-        label.y = r * dy + layout.cy;
+            state.x = r * dx + layout.cx;
+            state.y = r * dy + layout.cy;
 
-        const rotateType = getLabelAttr('rotate');
-        let rotate = 0;
-        if (rotateType === 'radial') {
-            rotate = -midAngle;
-            if (rotate < -Math.PI / 2) {
-                rotate += Math.PI;
+            const rotateType = getLabelAttr(labelStateModel, 'rotate');
+            let rotate = 0;
+            if (rotateType === 'radial') {
+                rotate = -midAngle;
+                if (rotate < -Math.PI / 2) {
+                    rotate += Math.PI;
+                }
             }
-        }
-        else if (rotateType === 'tangential') {
-            rotate = Math.PI / 2 - midAngle;
-            if (rotate > Math.PI / 2) {
-                rotate -= Math.PI;
+            else if (rotateType === 'tangential') {
+                rotate = Math.PI / 2 - midAngle;
+                if (rotate > Math.PI / 2) {
+                    rotate -= Math.PI;
+                }
+                else if (rotate < -Math.PI / 2) {
+                    rotate += Math.PI;
+                }
             }
-            else if (rotate < -Math.PI / 2) {
-                rotate += Math.PI;
+            else if (typeof rotateType === 'number') {
+                rotate = rotateType * Math.PI / 180;
             }
-        }
-        else if (typeof rotateType === 'number') {
-            rotate = rotateType * Math.PI / 180;
-        }
-        label.attr('rotation', rotate);
 
-        type LabelOption = SunburstSeriesNodeItemOption['label'];
-        function getLabelAttr<T extends keyof LabelOption>(name: T): 
LabelOption[T] {
-            const stateAttr = labelModel.get(name);
+            state.rotation = rotate;
+        });
+
+
+        type LabelOpt = SunburstSeriesOption['label'];
+        function getLabelAttr<T extends keyof LabelOpt>(model: 
Model<LabelOpt>, name: T): LabelOpt[T] {
+            const stateAttr = model.get(name);
             if (stateAttr == null) {
-                return normalModel.get(name);
-            }
-            else {
-                return stateAttr;
+                return normalLabelModel.get(name) as LabelOpt[T];
             }
+            return stateAttr;
         }
+
+        label.dirtyStyle();
     }
 
     _initEvents(
@@ -351,15 +329,13 @@ class SunburstPiece extends graphic.Group {
             that.onHighlight();
         };
 
-        if (seriesModel.isAnimationEnabled()) {
-            sector
-                .on('mouseover', onEmphasis)
-                .on('mouseout', onNormal)
-                .on('emphasis', onEmphasis)
-                .on('normal', onNormal)
-                .on('downplay', onDownplay)
-                .on('highlight', onHighlight);
-        }
+        sector
+            .on('mouseover', onEmphasis)
+            .on('mouseout', onNormal)
+            .on('emphasis', onEmphasis)
+            .on('normal', onNormal)
+            .on('downplay', onDownplay)
+            .on('highlight', onHighlight);
     }
 
 }
diff --git a/src/chart/sunburst/SunburstSeries.ts 
b/src/chart/sunburst/SunburstSeries.ts
index 327a1ab..0b4fdd6 100644
--- a/src/chart/sunburst/SunburstSeries.ts
+++ b/src/chart/sunburst/SunburstSeries.ts
@@ -242,7 +242,10 @@ class SunburstSeriesModel extends 
SeriesModel<SunburstSeriesOption> {
         },
         downplay: {
             itemStyle: {
-                opacity: 0.9
+                opacity: 0.5
+            },
+            label: {
+                opacity: 0.6
             }
         },
 
diff --git a/src/chart/sunburst/SunburstView.ts 
b/src/chart/sunburst/SunburstView.ts
index bef20f9..6a3e4f8 100644
--- a/src/chart/sunburst/SunburstView.ts
+++ b/src/chart/sunburst/SunburstView.ts
@@ -130,7 +130,7 @@ class SunburstView extends ChartView {
                     if (newNode) {
                         // Update
                         oldNode.piece.updateData(
-                            false, newNode, 'normal', seriesModel, ecModel);
+                            false, newNode, seriesModel, ecModel);
 
                         // For tooltip
                         data.setItemGraphicEl(newNode.dataIndex, 
oldNode.piece);
@@ -172,7 +172,7 @@ class SunburstView extends ChartView {
                 if (self.virtualPiece) {
                     // Update
                     self.virtualPiece.updateData(
-                        false, virtualRoot, 'normal', seriesModel, ecModel);
+                        false, virtualRoot, seriesModel, ecModel);
                 }
                 else {
                     // Add
@@ -208,7 +208,7 @@ class SunburstView extends ChartView {
             const viewRoot = this.seriesModel.getViewRoot();
             viewRoot.eachNode((node: DrawTreeNode) => {
                 if (!targetFound
-                    && node.piece && node.piece.childAt(0) === e.target
+                    && node.piece && node.piece === e.target
                 ) {
                     const nodeClick = 
node.getModel<SunburstSeriesNodeItemOption>().get('nodeClick');
                     if (nodeClick === 'rootToNode') {
diff --git a/src/echarts.ts b/src/echarts.ts
index b92fcd7..ddca382 100644
--- a/src/echarts.ts
+++ b/src/echarts.ts
@@ -1673,6 +1673,8 @@ class ECharts extends Eventful {
 
                 updateBlend(seriesModel, chartView);
 
+                updateStates(seriesModel, chartView);
+
                 updateHoverEmphasisHandler(chartView);
 
                 // Add albels.
@@ -1681,8 +1683,8 @@ class ECharts extends Eventful {
 
             scheduler.unfinished = unfinished || scheduler.unfinished;
 
-            labelManager.updateLayoutConfig(api);
-            labelManager.layout();
+            // labelManager.updateLayoutConfig(api);
+            // labelManager.layout();
 
             // If use hover layer
             updateHoverLayerStatus(ecIns, ecModel);
@@ -1753,7 +1755,7 @@ class ECharts extends Eventful {
             const zlevel = model.get('zlevel');
             // Set z and zlevel
             view.group.traverse(function (el: Displayable) {
-                if (el.type !== 'group') {
+                if (!el.isGroup) {
                     z != null && (el.z = z);
                     zlevel != null && (el.zlevel = zlevel);
 
@@ -1769,6 +1771,22 @@ class ECharts extends Eventful {
             });
         };
 
+        updateStates = function (seriesModel: SeriesModel, view: ChartView): 
void {
+            const stateAnimationModel = seriesModel.getModel('stateAnimation');
+            const enableAnimation = seriesModel.isAnimationEnabled();
+            view.group.traverse(function (el: Displayable) {
+                if (el.states && el.states.emphasis) {
+                    if (enableAnimation) {
+                        // TODO textContent?
+                        graphic.setStateTransition(el, stateAnimationModel);
+                    }
+                    else if (el.stateTransition) {
+                        el.stateTransition = null;
+                    }
+                }
+            });
+        };
+
         function getHighDownDispatcher(target: Element) {
             while (target && !graphic.isHighDownDispatcher(target)) {
                 target = target.parent;
@@ -1888,6 +1906,7 @@ let renderSeries: (
 let performPostUpdateFuncs: (ecModel: GlobalModel, api: ExtensionAPI) => void;
 let updateHoverLayerStatus: (ecIns: ECharts, ecModel: GlobalModel) => void;
 let updateBlend: (seriesModel: SeriesModel, chartView: ChartView) => void;
+let updateStates: (model: SeriesModel, chartView: ChartView) => void;
 let updateZ: (model: ComponentModel, view: ComponentView | ChartView) => void;
 let updateHoverEmphasisHandler: (view: ComponentView | ChartView) => void;
 let createExtensionAPI: (ecIns: ECharts) => ExtensionAPI;
diff --git a/src/model/Model.ts b/src/model/Model.ts
index 3f42f72..a2f588f 100644
--- a/src/model/Model.ts
+++ b/src/model/Model.ts
@@ -169,37 +169,37 @@ class Model<Opt extends ModelOption = ModelOption> {    
// TODO: TYPE use unkown
      *
      * @param deepMerge If do deep merge. Default to be false.
      */
-    squash(
-        deepMerge?: boolean,
-        handleCallback?: (func: () => object) => object
-    ) {
-        const optionStack = [];
-        let model: Model = this;
-        while (model) {
-            if (model.option) {
-                optionStack.push(model.option);
-            }
-            model = model.parentModel;
-        }
-
-        const newOption = {} as Opt;
-        let option;
-        while (option = optionStack.pop()) {    // Top down merge
-            if (isFunction(option) && handleCallback) {
-                option = handleCallback(option);
-            }
-            if (deepMerge) {
-                merge(newOption, option);
-            }
-            else {
-                extend(newOption, option);
-            }
-        }
-
-        // Remove parentModel
-        this.option = newOption;
-        this.parentModel = null;
-    }
+    // squash(
+    //     deepMerge?: boolean,
+    //     handleCallback?: (func: () => object) => object
+    // ) {
+    //     const optionStack = [];
+    //     let model: Model = this;
+    //     while (model) {
+    //         if (model.option) {
+    //             optionStack.push(model.option);
+    //         }
+    //         model = model.parentModel;
+    //     }
+
+    //     const newOption = {} as Opt;
+    //     let option;
+    //     while (option = optionStack.pop()) {    // Top down merge
+    //         if (isFunction(option) && handleCallback) {
+    //             option = handleCallback(option);
+    //         }
+    //         if (deepMerge) {
+    //             merge(newOption, option);
+    //         }
+    //         else {
+    //             extend(newOption, option);
+    //         }
+    //     }
+
+    //     // Remove parentModel
+    //     this.option = newOption;
+    //     this.parentModel = null;
+    // }
 
     /**
      * If model has option
diff --git a/src/model/Series.ts b/src/model/Series.ts
index fbceb6f..bce6b82 100644
--- a/src/model/Series.ts
+++ b/src/model/Series.ts
@@ -534,10 +534,7 @@ class SeriesModel<Opt extends SeriesOption = SeriesOption> 
extends ComponentMode
         };
     }
 
-    /**
-     * @return {boolean}
-     */
-    isAnimationEnabled() {
+    isAnimationEnabled(): boolean {
         if (env.node) {
             return false;
         }
@@ -547,7 +544,7 @@ class SeriesModel<Opt extends SeriesOption = SeriesOption> 
extends ComponentMode
                 animationEnabled = false;
             }
         }
-        return animationEnabled;
+        return !!animationEnabled;
     }
 
     restoreData() {
diff --git a/src/model/globalDefault.ts b/src/model/globalDefault.ts
index ad2199b..fe4feb3 100644
--- a/src/model/globalDefault.ts
+++ b/src/model/globalDefault.ts
@@ -59,13 +59,19 @@ export default {
     // Default is source-over
     blendMode: null,
 
+    stateAnimation: {
+        duration: 300,
+        easing: 'cubicOut'
+    },
+
     animation: 'auto',
     animationDuration: 1000,
     animationDurationUpdate: 300,
-    animationEasing: 'exponentialOut',
+    animationEasing: 'cubicOut',
     animationEasingUpdate: 'cubicOut',
 
     animationThreshold: 2000,
+
     // Configuration for progressive/incremental rendering
     progressiveThreshold: 3000,
     progressive: 400,
diff --git a/src/util/graphic.ts b/src/util/graphic.ts
index ea75df7..182b69c 100644
--- a/src/util/graphic.ts
+++ b/src/util/graphic.ts
@@ -60,7 +60,8 @@ import {
     ColorString,
     DataModel,
     ECEventData,
-    ZRStyleProps
+    ZRStyleProps,
+    AnimationOption
 } from './types';
 import GlobalModel from '../model/Global';
 import { makeInner } from './model';
@@ -127,8 +128,6 @@ type TextCommonParams = {
      */
     autoColor?: ColorString
 
-    forceRich?: boolean
-
     getTextPosition?: (textStyleModel: Model, isEmphasis?: boolean) => string 
| string[] | number[]
 
     defaultOutsidePosition?: LabelOption['position']
@@ -379,19 +378,13 @@ function singleEnterEmphasis(el: Element) {
         return;
     }
 
-    el.useState('emphasis', true, {
-        duration: 300,
-        // TODO Configuration
-        easing: 'cubicOut'
-    });
+    el.useState('emphasis', true);
     // TODO hover layer
 }
 
 
 function singleLeaveEmphasis(el: Element) {
-    el.removeState('emphasis', {
-        duration: 300
-    });
+    el.removeState('emphasis');
     (el as ExtendedElement).__highlighted = false;
 }
 
@@ -549,6 +542,23 @@ export function enableHoverEmphasis(el: Element, 
hoverStyle?: ZRStyleProps) {
 }
 
 /**
+ * Set animation config on state transition.
+ */
+export function setStateTransition(el: Element, animatableModel: 
Model<AnimationOption>) {
+    const duration = animatableModel.get('duration');
+    if (duration > 0) {
+        el.stateTransition = {
+            duration,
+            delay: animatableModel.get('delay'),
+            easing: animatableModel.get('easing')
+        };
+    }
+    else if (el.stateTransition) {
+        el.stateTransition = null;
+    }
+}
+
+/**
  * @param {module:zrender/Element} el
  * @param {Function} [el.onStateChange] Called when state updated.
  *        Since `setHoverStyle` has the constraint that it must be called after
@@ -648,37 +658,6 @@ interface SetLabelStyleOpt<LDI> extends TextCommonParams {
 }
 
 
-// function handleSquashCallback<LDI>(
-//     func: Function,
-//     labelDataIndex: LDI,
-//     labelFetcher: SetLabelStyleOpt<LDI>['labelFetcher'],
-//     rect: RectLike,
-//     status: DisplayState
-// ) {
-//     let params: {
-//         status?: DisplayState
-//         rect?: RectLike
-//     };
-//     if (labelFetcher && labelFetcher.getDataParams) {
-//         params = labelFetcher.getDataParams(labelDataIndex);
-//     }
-//     else {
-//         params = {};
-//     }
-//     params.status = status;
-//     params.rect = rect;
-//     return func(params);
-// }
-
-// function getGlobalBoundingRect(el: Element) {
-//     const rect = el.getBoundingRect().clone();
-//     const transform = el.getComputedTransform();
-//     if (transform) {
-//         rect.applyTransform(transform);
-//     }
-//     return rect;
-// }
-
 type LabelModel = Model<LabelOption & {
     formatter?: string | ((params: any) => string)
 }>;
@@ -717,33 +696,9 @@ function setLabelStyle<LDI>(
     const labelDataIndex = opt.labelDataIndex;
     const labelDimIndex = opt.labelDimIndex;
 
-    // TODO Performance optimization
-    // normalModel.squash(false, function (func: Function) {
-    //     return handleSquashCallback(
-    //         func,
-    //         labelDataIndex,
-    //         labelFetcher,
-    //         isSetOnText ? null : getGlobalBoundingRect(targetEl),
-    //         'normal'
-    //     );
-    // });
-
-    // emphasisModel.squash(false, function (func: Function) {
-    //     return handleSquashCallback(
-    //         func,
-    //         labelDataIndex,
-    //         labelFetcher,
-    //         isSetOnText ? null : getGlobalBoundingRect(targetEl),
-    //         'emphasis'
-    //     );
-    // });
-
     const showNormal = normalModel.getShallow('show');
     const showEmphasis = emphasisModel.getShallow('show');
 
-    // Consider performance, only fetch label when necessary.
-    // If `normal.show` is `false` and `emphasis.show` is `true` and 
`emphasis.formatter` is not set,
-    // label should be displayed, where text is fetched by `normal.formatter` 
or `opt.defaultText`.
     let richText = isSetOnText ? targetEl as ZRText : null;
     if (showNormal || showEmphasis) {
         let baseText;
@@ -780,12 +735,6 @@ function setLabelStyle<LDI>(
         const emphasisState = richText.ensureState('emphasis');
         emphasisState.ignore = !showEmphasis;
 
-        // Always set `textStyle` even if `normalStyle.text` is null, because 
default
-        // values have to be set on `normalStyle`.
-        // If we set default values on `emphasisStyle`, consider case:
-        // Firstly, `setOption(... label: {normal: {text: null}, emphasis: 
{show: true}} ...);`
-        // Secondly, `setOption(... label: {noraml: {show: true, text: 'abc', 
color: 'red'} ...);`
-        // Then the 'red' will not work on emphasis.
         const normalStyle = createTextStyle(
             normalModel,
             normalSpecified,
@@ -848,12 +797,12 @@ export {setLabelStyle};
 export function createTextStyle(
     textStyleModel: Model,
     specifiedTextStyle?: TextStyleProps,    // Can be overrided by settings in 
model.
-    opt?: TextCommonParams,
-    isEmphasis?: boolean,
+    opt?: Pick<TextCommonParams, 'autoColor' | 'disableBox'>,
+    isNotNormal?: boolean,
     isAttached?: boolean // If text is attached on an element. If so, auto 
color will handling in zrender.
 ) {
     const textStyle: TextStyleProps = {};
-    setTextStyleCommon(textStyle, textStyleModel, opt, isEmphasis, isAttached);
+    setTextStyleCommon(textStyle, textStyleModel, opt, isNotNormal, 
isAttached);
     specifiedTextStyle && extend(textStyle, specifiedTextStyle);
     // textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false);
 
@@ -863,25 +812,25 @@ export function createTextStyle(
 export function createTextConfig(
     textStyle: TextStyleProps,
     textStyleModel: Model,
-    opt?: TextCommonParams,
-    isEmphasis?: boolean
+    opt?: Pick<TextCommonParams, 'getTextPosition' | 'defaultOutsidePosition' 
| 'autoColor'>,
+    isNotNormal?: boolean
 ) {
     const textConfig: ElementTextConfig = {};
     let labelPosition;
     let labelRotate = textStyleModel.getShallow('rotate');
     const labelDistance = retrieve2(
-        textStyleModel.getShallow('distance'), isEmphasis ? null : 5
+        textStyleModel.getShallow('distance'), isNotNormal ? null : 5
     );
     const labelOffset = textStyleModel.getShallow('offset');
 
     if (opt.getTextPosition) {
-        labelPosition = opt.getTextPosition(textStyleModel, isEmphasis);
+        labelPosition = opt.getTextPosition(textStyleModel, isNotNormal);
     }
     else {
         labelPosition = textStyleModel.getShallow('position')
-            || (isEmphasis ? null : 'inside');
+            || (isNotNormal ? null : 'inside');
         // 'outside' is not a valid zr textPostion value, but used
-        // in bar series, and magric type should be considered.
+        // in bar series, and magic type should be considered.
         labelPosition === 'outside' && (labelPosition = 
opt.defaultOutsidePosition || 'top');
     }
 
@@ -905,17 +854,6 @@ export function createTextConfig(
     // Set default stroke, which is useful when label is over other
     // messy graphics (like lines) in background.
     textConfig.outsideStroke = 'rgba(255, 255, 255, 0.9)';
-    // if (!textStyle.fill) {
-    //     textConfig.insideFill = 'auto';
-    //     textConfig.outsideFill = opt.autoColor || null;
-    // }
-    // if (!textStyle.stroke) {
-    //     textConfig.insideStroke = 'auto';
-    // }
-    // else if (opt.autoColor) {
-    //     // TODO: stroke set to autoColor. if label is inside?
-    //     textConfig.insideStroke = opt.autoColor;
-    // }
 
     return textConfig;
 }
@@ -933,8 +871,8 @@ export function createTextConfig(
 function setTextStyleCommon(
     textStyle: TextStyleProps,
     textStyleModel: Model,
-    opt?: TextCommonParams,
-    isEmphasis?: boolean,
+    opt?: Pick<TextCommonParams, 'autoColor' | 'disableBox'>,
+    isNotNormal?: boolean,
     isAttached?: boolean
 ) {
     // Consider there will be abnormal when merge hover style to normal style 
if given default value.
@@ -970,7 +908,7 @@ function setTextStyleCommon(
                 // the default color `'blue'` will not be adopted if no color 
declared in `rich`.
                 // That might confuses users. So probably we should put 
`textStyleModel` as the
                 // root ancestor of the `richTextStyle`. But that would be a 
break change.
-                setTokenTextStyle(richResult[name] = {}, richTextStyle, 
globalTextStyle, opt, isEmphasis, isAttached);
+                setTokenTextStyle(richResult[name] = {}, richTextStyle, 
globalTextStyle, opt, isNotNormal, isAttached);
             }
         }
     }
@@ -983,12 +921,7 @@ function setTextStyleCommon(
         textStyle.overflow = overflow;
     }
 
-    setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, 
isEmphasis, isAttached, true);
-
-    // TODO
-    if (opt.forceRich && !opt.textStyle) {
-        opt.textStyle = {};
-    }
+    setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, 
isNotNormal, isAttached, true);
 }
 
 // Consider case:
@@ -1025,7 +958,7 @@ function getRichItemNames(textStyleModel: 
Model<LabelOption>) {
 }
 
 const TEXT_PROPS_WITH_GLOBAL = [
-    'fontStyle', 'fontWeight', 'fontSize', 'fontFamily',
+    'fontStyle', 'fontWeight', 'fontSize', 'fontFamily', 'opacity',
     'textShadowColor', 'textShadowBlur', 'textShadowOffsetX', 
'textShadowOffsetY'
 ] as const;
 
@@ -1043,13 +976,13 @@ function setTokenTextStyle(
     textStyle: TextStyleProps['rich'][string],
     textStyleModel: Model<LabelOption>,
     globalTextStyle: LabelOption,
-    opt?: TextCommonParams,
-    isEmphasis?: boolean,
+    opt?: Pick<TextCommonParams, 'autoColor' | 'disableBox'>,
+    isNotNormal?: boolean,
     isAttached?: boolean,
     isBlock?: boolean
 ) {
     // In merge mode, default value should not be given.
-    globalTextStyle = !isEmphasis && globalTextStyle || EMPTY_OBJ;
+    globalTextStyle = !isNotNormal && globalTextStyle || EMPTY_OBJ;
 
     const autoColor = opt && opt.autoColor;
     let fillColor = textStyleModel.getShallow('color');
@@ -1078,7 +1011,7 @@ function setTokenTextStyle(
     }
 
     // TODO
-    if (!isEmphasis && !isAttached) {
+    if (!isNotNormal && !isAttached) {
         // Set default finally.
         if (textStyle.fill == null && opt.autoColor) {
             textStyle.fill = opt.autoColor;
diff --git a/src/util/types.ts b/src/util/types.ts
index ea3c3b8..d9bbd1b 100644
--- a/src/util/types.ts
+++ b/src/util/types.ts
@@ -546,6 +546,11 @@ export type AnimationDelayCallbackParam = {
 export type AnimationDurationCallback = (idx: number) => number;
 export type AnimationDelayCallback = (idx: number, params?: 
AnimationDelayCallbackParam) => number;
 
+export interface AnimationOption {
+    duration?: number
+    easing?: AnimationEasing
+    delay?: number
+}
 /**
  * Mixin of option set to control the animation of series.
  */
@@ -1122,6 +1127,11 @@ export interface SeriesOption extends
      * Global label layout option in label layout stage.
      */
     labelLayout?: LabelLayoutOption | LabelLayoutOptionCallback
+
+    /**
+     * Animation config for state transition.
+     */
+    stateAnimation?: AnimationOption
 }
 
 export interface SeriesOnCartesianOptionMixin {
diff --git a/src/visual/style.ts b/src/visual/style.ts
index 605edee..1d52fcf 100644
--- a/src/visual/style.ts
+++ b/src/visual/style.ts
@@ -187,7 +187,7 @@ const dataColorPaletteTask: StageHandler = {
                 idxMap[rawIdx] = idx;
             });
 
-            // Iterate on dat before filtered. To make sure color from palette 
can be
+            // Iterate on data before filtered. To make sure color from 
palette can be
             // Consistent when toggling legend.
             dataAll.each(function (rawIdx) {
                 const idx = idxMap[rawIdx];


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to