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

commit 34e4cb0f6a282b493abef79426cc09083780e8f7
Author: pissang <bm2736...@gmail.com>
AuthorDate: Thu May 28 13:39:34 2020 +0800

    feat: label and labelLine animation
---
 src/chart/pie/PieView.ts      |  31 ++++-------
 src/chart/pie/labelLayout.ts  |   5 ++
 src/echarts.ts                |  15 +++---
 src/label/LabelManager.ts     | 118 ++++++++++++++++++++++++++++++++++--------
 src/label/labelGuideHelper.ts |   2 +-
 5 files changed, 120 insertions(+), 51 deletions(-)

diff --git a/src/chart/pie/PieView.ts b/src/chart/pie/PieView.ts
index 0f2865a..8187c6a 100644
--- a/src/chart/pie/PieView.ts
+++ b/src/chart/pie/PieView.ts
@@ -136,9 +136,7 @@ class PiePiece extends graphic.Sector {
         const cursorStyle = itemModel.getShallow('cursor');
         cursorStyle && sector.attr('cursor', cursorStyle);
 
-        // Label and text animation should be applied only for transition type 
animation when update
-        const withAnimation = !firstCreate && animationTypeUpdate === 
'transition';
-        this._updateLabel(data, idx, withAnimation);
+        this._updateLabel(data, idx);
 
         const emphasisState = sector.ensureState('emphasis');
         emphasisState.shape = {
@@ -165,12 +163,11 @@ class PiePiece extends graphic.Sector {
         (sector as ECElement).selected = 
seriesModel.isSelected(data.getName(idx));
     }
 
-    private _updateLabel(data: List, idx: number, withAnimation: boolean): 
void {
+    private _updateLabel(data: List, idx: number): void {
         const sector = this;
         const labelLine = sector.getTextGuideLine();
         const labelText = sector.getTextContent();
 
-        const seriesModel = data.hostModel;
         const itemModel = data.getItemModel<PieDataItemOption>(idx);
         const layout = data.getItemLayout(idx);
         const labelLayout = layout.label;
@@ -225,25 +222,15 @@ class PiePiece extends graphic.Sector {
             outsideFill: visualColor
         });
 
-        const targetTextPos = {
+        labelLine.attr({
+            shape: targetLineShape
+        });
+        // Make sure update style on labelText after setLabelStyle.
+        // Because setLabelStyle will replace a new style on it.
+        labelText.attr({
             x: labelLayout.x,
             y: labelLayout.y
-        };
-        if (withAnimation) {
-            graphic.updateProps(labelLine, {
-                shape: targetLineShape
-            }, seriesModel, idx);
-
-            graphic.updateProps(labelText, targetTextPos, seriesModel, idx);
-        }
-        else {
-            labelLine.attr({
-                shape: targetLineShape
-            });
-            // Make sure update style on labelText after setLabelStyle.
-            // Because setLabelStyle will replace a new style on it.
-            labelText.attr(targetTextPos);
-        }
+        });
 
         labelText.attr({
             rotation: labelLayout.rotation,
diff --git a/src/chart/pie/labelLayout.ts b/src/chart/pie/labelLayout.ts
index 04542b5..d1da84d 100644
--- a/src/chart/pie/labelLayout.ts
+++ b/src/chart/pie/labelLayout.ts
@@ -323,6 +323,11 @@ export default function (
         const text = seriesModel.getFormattedLabel(idx, 'normal')
                 || data.getName(idx);
         const textRect = textContain.getBoundingRect(text, font, textAlign, 
'top');
+        // Text has a default 1px stroke. Exclude this.
+        textRect.x -= 1;
+        textRect.y -= 1;
+        textRect.width += 2.1;
+        textRect.height += 2.1;
 
         const isLabelInside = labelPosition === 'inside' || labelPosition === 
'inner';
         if (labelPosition === 'center') {
diff --git a/src/echarts.ts b/src/echarts.ts
index 6723c9d..3a68ce7 100644
--- a/src/echarts.ts
+++ b/src/echarts.ts
@@ -1097,6 +1097,7 @@ class ECharts extends Eventful {
         const labelManager = this._labelManager;
         labelManager.updateLayoutConfig(this._api);
         labelManager.layout();
+        labelManager.animateLabels();
     }
 
     appendData(params: {
@@ -1722,16 +1723,20 @@ class ECharts extends Eventful {
                 // Add labels.
                 labelManager.addLabelsOfSeries(chartView);
 
-                // NOTE: Update states after label is added.
-                // Because in LabelManager#addLabel. It will cache the 
properties(transform, textConfig) of label.
-                // We need to cache the normal state. Not other states.
-                updateStates(seriesModel, chartView);
             });
 
             scheduler.unfinished = unfinished || scheduler.unfinished;
 
             labelManager.updateLayoutConfig(api);
             labelManager.layout();
+            labelManager.animateLabels();
+
+            ecModel.eachSeries(function (seriesModel) {
+                const chartView = ecIns._chartsMap[seriesModel.__viewId];
+                // NOTE: Update states after label is updated.
+                // label should be in normal status when layouting.
+                updateStates(seriesModel, chartView);
+            });
 
             // If use hover layer
             // TODO
@@ -1877,8 +1882,6 @@ class ECharts extends Eventful {
                         states.push('emphasis');
                     }
                     el.useStates(states);
-                    // el.toggleState('select', (el as ECElement).selected);
-                    // el.toggleState('emphasis', (el as 
ECElement).highlighted);
                 }
             });
         };
diff --git a/src/label/LabelManager.ts b/src/label/LabelManager.ts
index 8d203e4..3cbdef5 100644
--- a/src/label/LabelManager.ts
+++ b/src/label/LabelManager.ts
@@ -25,7 +25,9 @@ import {
     Point,
     BoundingRect,
     getECData,
-    Polyline
+    Polyline,
+    updateProps,
+    initProps
 } from '../util/graphic';
 import { MatrixArray } from 'zrender/src/core/matrix';
 import ExtensionAPI from '../ExtensionAPI';
@@ -38,10 +40,13 @@ import {
 } from '../util/types';
 import { parsePercent } from '../util/number';
 import ChartView from '../view/Chart';
-import { ElementTextConfig, ElementTextGuideLineConfig } from 
'zrender/src/Element';
+import { ElementTextConfig } from 'zrender/src/Element';
 import { RectLike } from 'zrender/src/core/BoundingRect';
 import Transformable from 'zrender/src/core/Transformable';
 import { updateLabelGuideLine } from './labelGuideHelper';
+import SeriesModel from '../model/Series';
+import { makeInner } from '../util/model';
+import { retrieve2, guid, each } from 'zrender/src/core/util';
 
 interface DisplayedLabelItem {
     label: ZRText
@@ -56,7 +61,7 @@ interface LabelLayoutDesc {
     label: ZRText
     labelGuide: Polyline
 
-    seriesIndex: number
+    seriesModel: SeriesModel
     dataIndex: number
 
     layoutOption: LabelLayoutOptionCallback | LabelLayoutOption
@@ -100,7 +105,7 @@ function prepareLayoutCallbackParams(labelItem: 
LabelLayoutDesc): LabelLayoutOpt
     const label = labelItem.label;
     return {
         dataIndex: labelItem.dataIndex,
-        seriesIndex: labelItem.seriesIndex,
+        seriesIndex: labelItem.seriesModel.seriesIndex,
         text: labelItem.label.style.text,
         rect: labelItem.hostRect,
         labelRect: labelAttr.rect,
@@ -115,26 +120,38 @@ const LABEL_OPTION_TO_STYLE_KEYS = ['align', 
'verticalAlign', 'width', 'height']
 
 const dummyTransformable = new Transformable();
 
+const labelAnimationStore = makeInner<{
+    oldLayout: {
+        x: number,
+        y: number,
+        rotation: number
+    }
+}, ZRText>();
+
+const labelLineAnimationStore = makeInner<{
+    oldLayout: {
+        points: number[][]
+    }
+}, Polyline>();
+
 class LabelManager {
 
     private _labelList: LabelLayoutDesc[] = [];
+    private _chartViewList: ChartView[] = [];
 
     constructor() {}
 
     clearLabels() {
         this._labelList = [];
+        this._chartViewList = [];
     }
 
     /**
      * Add label to manager
-     * @param dataIndex
-     * @param seriesIndex
-     * @param label
-     * @param layoutOption
      */
     addLabel(
         dataIndex: number,
-        seriesIndex: number,
+        seriesModel: SeriesModel,
         label: ZRText,
         layoutOption: LabelLayoutDesc['layoutOption']
     ) {
@@ -171,7 +188,7 @@ class LabelManager {
             label,
             labelGuide: labelGuide,
 
-            seriesIndex,
+            seriesModel,
             dataIndex,
 
             layoutOption,
@@ -215,6 +232,8 @@ class LabelManager {
     }
 
     addLabelsOfSeries(chartView: ChartView) {
+        this._chartViewList.push(chartView);
+
         const seriesModel = chartView.__model;
         const layoutOption = seriesModel.get('labelLayout');
         chartView.group.traverse((child) => {
@@ -226,7 +245,7 @@ class LabelManager {
             const textEl = child.getTextContent();
             const dataIndex = getECData(child).dataIndex;
             if (textEl && dataIndex != null) {
-                this.addLabel(dataIndex, seriesModel.seriesIndex, textEl, 
layoutOption);
+                this.addLabel(dataIndex, seriesModel, textEl, layoutOption);
             }
         });
     }
@@ -251,6 +270,7 @@ class LabelManager {
             }
 
             layoutOption = layoutOption || {};
+
             if (hostEl) {
                 hostEl.setTextConfig({
                     // Force to set local false.
@@ -288,10 +308,7 @@ class LabelManager {
 
             for (let k = 0; k < LABEL_OPTION_TO_STYLE_KEYS.length; k++) {
                 const key = LABEL_OPTION_TO_STYLE_KEYS[k];
-                label.setStyle(
-                    key,
-                    layoutOption[key] != null ? layoutOption[key] : 
defaultLabelAttr.style[key]
-                );
+                label.setStyle(key, layoutOption[key] != null ? 
layoutOption[key] : defaultLabelAttr.style[key]);
             }
 
             labelItem.overlap = layoutOption.overlap;
@@ -325,9 +342,6 @@ class LabelManager {
 
             const globalRect = localRect.clone();
             globalRect.applyTransform(transform);
-            // Text has a default 1px stroke. Exclude this.
-            globalRect.width -= 3;
-            globalRect.height -= 3;
 
             let obb = isAxisAligned ? new OrientedBoundingRect(localRect, 
transform) : null;
             let overlapped = false;
@@ -362,14 +376,10 @@ class LabelManager {
             const labelGuide = labelItem.labelGuide;
             // TODO Callback to determine if this overlap should be handled?
             if (overlapped) {
-                // label.setStyle({ opacity: 0.1 });
-                // label.z = 0;
                 label.hide();
                 labelGuide && labelGuide.hide();
             }
             else {
-                // TODO Restore z
-                // label.setStyle({ opacity: 1 });
                 label.attr('ignore', labelItem.defaultAttr.ignore);
                 labelGuide && labelGuide.attr('ignore', 
labelItem.defaultAttr.labelGuideIgnore);
 
@@ -391,6 +401,70 @@ class LabelManager {
             );
         }
     }
+
+    animateLabels() {
+        each(this._chartViewList, function (chartView) {
+            const seriesModel = chartView.__model;
+            if (!seriesModel.isAnimationEnabled()) {
+                return;
+            }
+
+            chartView.group.traverse((child) => {
+                if (child.ignore) {
+                    return true;    // Stop traverse descendants.
+                }
+
+                // Only support label being hosted on graphic elements.
+                const textEl = child.getTextContent();
+                const guideLine = child.getTextGuideLine();
+
+                if (textEl && !textEl.ignore && !textEl.invisible) {
+                    const layoutStore = labelAnimationStore(textEl);
+                    const oldLayout = layoutStore.oldLayout;
+                    const newProps = {
+                        x: textEl.x,
+                        y: textEl.y,
+                        rotation: textEl.rotation
+                    };
+                    if (!oldLayout) {
+                        textEl.attr(newProps);
+                        const oldOpacity = retrieve2(textEl.style.opacity, 1);
+                        // Fade in animation
+                        textEl.style.opacity = 0;
+                        initProps(textEl, {
+                            style: { opacity: oldOpacity }
+                        }, seriesModel);
+                    }
+                    else {
+                        textEl.attr(oldLayout);
+                        updateProps(textEl, newProps, seriesModel);
+                    }
+                    layoutStore.oldLayout = newProps;
+                }
+
+                if (guideLine && !guideLine.ignore && !guideLine.invisible) {
+                    const layoutStore = labelLineAnimationStore(guideLine);
+                    const oldLayout = layoutStore.oldLayout;
+                    const newLayout = { points: guideLine.shape.points };
+                    if (!oldLayout) {
+                        guideLine.setShape(newLayout);
+                        guideLine.style.strokePercent = 0;
+                        initProps(guideLine, {
+                            style: { strokePercent: 1 }
+                        }, seriesModel);
+                    }
+                    else {
+                        guideLine.attr({ shape: oldLayout });
+                        updateProps(guideLine, {
+                            shape: newLayout
+                        }, seriesModel);
+                    }
+
+                    layoutStore.oldLayout = newLayout;
+                }
+            });
+        });
+    }
 }
 
 
diff --git a/src/label/labelGuideHelper.ts b/src/label/labelGuideHelper.ts
index b20511a..da4848b 100644
--- a/src/label/labelGuideHelper.ts
+++ b/src/label/labelGuideHelper.ts
@@ -346,7 +346,7 @@ export function updateLabelGuideLine(
     for (let i = 0; i < searchSpace.length; i++) {
         const candidate = searchSpace[i];
         getCandidateAnchor(candidate, 0, labelRect, pt0, dir);
-        Point.scaleAndAdd(pt1, pt0, dir, labelGuideConfig.len);
+        Point.scaleAndAdd(pt1, pt0, dir, labelGuideConfig.len == null ? 15 : 
labelGuideConfig.len);
 
         const dist = anchorPoint ? anchorPoint.distance(pt1)
             : (target instanceof Path


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

Reply via email to