You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by su...@apache.org on 2020/06/08 14:22:59 UTC

[incubator-echarts] 02/03: feature: custom series during callback params re-design.

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

sushuang pushed a commit to branch custom-series-enhance
in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git

commit 6c227d3ab5bc01a21d40b0827fbd623e80b85fc4
Author: 100pah <su...@gmail.com>
AuthorDate: Mon Jun 8 12:28:59 2020 +0800

    feature: custom series during callback params re-design.
---
 src/chart/custom.ts         | 110 ++++++++++++++++++++++++--------------------
 test/custom-transition.html |  63 +++++++++++++++----------
 2 files changed, 97 insertions(+), 76 deletions(-)

diff --git a/src/chart/custom.ts b/src/chart/custom.ts
index b35f55b..9125b97 100644
--- a/src/chart/custom.ts
+++ b/src/chart/custom.ts
@@ -103,6 +103,7 @@ const TRANSFORM_PROPS = {
     rotation: 1
 } as const;
 type TransformProps = keyof typeof TRANSFORM_PROPS;
+const transformPropNamesStr = keys(TRANSFORM_PROPS).join(', ');
 
 type TransitionAnyProps = string | string[];
 type TransitionTransformProps = TransformProps | TransformProps[];
@@ -134,7 +135,7 @@ interface CustomBaseElementOption extends Partial<Pick<
     // Shape can be set in any el option for custom prop for annimation duration.
     shape?: TransitionAnyOption;
     // updateDuringAnimation
-    during?(elProps: CustomDuringElProps): void;
+    during?(params: typeof customDuringAPI): void;
 };
 interface CustomDisplayableOption extends CustomBaseElementOption, Partial<Pick<
     Displayable, 'zlevel' | 'z' | 'z2' | 'invisible'
@@ -162,10 +163,6 @@ interface CustomGroupOption extends CustomBaseElementOption {
 interface CustomZRPathOption extends CustomDisplayableOption {
     shape?: PathProps['shape'] & TransitionAnyOption;
 }
-interface CustomDuringElProps extends Partial<Pick<Element, TransformProps>> {
-    shape?: PathProps['shape'];
-    style?: { text: string };
-}
 interface CustomSVGPathOption extends CustomDisplayableOption {
     type: 'path';
     shape?: {
@@ -319,9 +316,6 @@ const Z2_SPECIFIED_BIT = {
     emphasis: 1
 } as const;
 
-const tmpDuringStyle = {} as CustomDuringElProps['style'];
-const tmpDuringElProps = {} as CustomDuringElProps;
-
 const LEGACY_TRANSFORM_PROPS = {
     position: ['x', 'y'],
     scale: ['scaleX', 'scaleY'],
@@ -931,54 +925,68 @@ function getOrCreateLeaveToPropsFromEl(el: Element): LooseElementProps {
     return innerEl.leaveToProps || (innerEl.leaveToProps = {});
 }
 
+// Use it to avoid it be exposed to user.
+const tmpDuringScope = {} as {
+    el: Element;
+    isShapeDirty: boolean;
+    isStyleDirty: boolean;
+};
+const customDuringAPI = {
+    // Usually other props do not need to be changed in animation during.
+    setAttr(key: TransformProps, val: unknown): void {
+        assert(hasOwn(TRANSFORM_PROPS, key), 'Only ' + transformPropNamesStr + ' available in `setAttr`.');
+        tmpDuringScope.el[key] = val as number;
+    },
+    getAttr(key: TransformProps): unknown {
+        assert(hasOwn(TRANSFORM_PROPS, key), 'Only ' + transformPropNamesStr + ' available in `getAttr`.');
+        return tmpDuringScope.el[key];
+    },
+    setShape(key: string, val: unknown): void {
+        // In custom series, el other than Path can also has `shape` for intepolating props.
+        const shape = (tmpDuringScope.el as any).shape || ((tmpDuringScope.el as any).shape = {});
+        shape[key] = val;
+        tmpDuringScope.isShapeDirty = true;
+    },
+    getShape(key: string): unknown {
+        const shape = (tmpDuringScope.el as any).shape;
+        if (shape) {
+            return shape[key];
+        }
+    },
+    setStyle(key: string, val: unknown): void {
+        const style = (tmpDuringScope.el as Displayable).style;
+        if (style) {
+            style[key] = val;
+            tmpDuringScope.isStyleDirty = true;
+        }
+    },
+    getStyle(key: string): unknown {
+        const style = (tmpDuringScope.el as Displayable).style;
+        if (style) {
+            return style[key];
+        }
+    }
+};
+
 function elUpdateDuringAnimation(this: Element, key: string): void {
     const innerEl = inner(this);
     // FIXME `this.markRedraw();` directly ?
     innerEl.orginalDuring.call(this, key);
     const customDuring = innerEl.customDuring;
-    const thisPath = this as graphicUtil.Path;
-    const thisText = this as graphicUtil.Text;
-    let dirtyStyle = false;
-
-    // Only provide these props. Usually other props do not need to be
-    // changed in animation during.
-    // Do not give `this` to user util really needed in future.
-    // Props in `shape` can be modified directly in the during callback.
-    const shapeCurr = tmpDuringElProps.shape = thisPath.shape;
-    const xCurr = tmpDuringElProps.x = this.x;
-    const yCurr = tmpDuringElProps.y = this.y;
-    const scaleXCurr = tmpDuringElProps.scaleX = this.scaleX;
-    const scaleYCurr = tmpDuringElProps.scaleY = this.scaleY;
-    const originXCurr = tmpDuringElProps.originX = this.originX;
-    const originYCurr = tmpDuringElProps.originY = this.originY;
-    const rotationCurr = tmpDuringElProps.rotation = this.rotation;
-
-    // PENDING:
-    // Do not expose other style in case that is not stable.
-    const isText = this.type === 'text';
-    // Always assign in case that user modify `.style`.
-    tmpDuringElProps.style = tmpDuringStyle;
-    const textCurr = tmpDuringStyle.text = isText ? thisText.style.text : null;
-
-    customDuring(tmpDuringElProps);
-
-    tmpDuringElProps.shape !== shapeCurr && (thisPath.shape = tmpDuringElProps.shape);
-    // Consider prop on prototype.
-    tmpDuringElProps.x !== xCurr && (this.x = tmpDuringElProps.x);
-    tmpDuringElProps.y !== yCurr && (this.y = tmpDuringElProps.y);
-    tmpDuringElProps.scaleX !== scaleXCurr && (this.scaleX = tmpDuringElProps.scaleX);
-    tmpDuringElProps.scaleY !== scaleYCurr && (this.scaleY = tmpDuringElProps.scaleY);
-    tmpDuringElProps.originX !== originXCurr && (this.originX = tmpDuringElProps.originX);
-    tmpDuringElProps.originY !== originYCurr && (this.originY = tmpDuringElProps.originY);
-    tmpDuringElProps.rotation !== rotationCurr && (this.rotation = tmpDuringElProps.rotation);
-
-    if (isText) {
-        const currTmpStl = tmpDuringElProps.style; // Allow user modify `.style`.
-        currTmpStl && currTmpStl.text !== textCurr && (thisText.style.text = currTmpStl.text, dirtyStyle = true);
-    }
-
-    dirtyStyle && this.dirty();
-    // markRedraw() will be called by default.
+
+    tmpDuringScope.el = this;
+    tmpDuringScope.isShapeDirty = false;
+    tmpDuringScope.isStyleDirty = false;
+
+    customDuring(customDuringAPI);
+
+    if (tmpDuringScope.isShapeDirty && (this as graphicUtil.Path).dirtyShape) {
+        (this as graphicUtil.Path).dirtyShape();
+    }
+    if (tmpDuringScope.isStyleDirty && (this as Displayable).dirtyStyle) {
+        (this as Displayable).dirtyStyle();
+    }
+    // markRedraw() will be called by default in during.
 
     // FIXME: if in future meet the case that some prop will be both modified in `during` and `state`,
     // consider the issue that the prop might be incorrect when return to "normal" state.
diff --git a/test/custom-transition.html b/test/custom-transition.html
index 2d0f762..a51d31d 100644
--- a/test/custom-transition.html
+++ b/test/custom-transition.html
@@ -40,7 +40,7 @@ under the License.
         <div id="spiral-fixed-extent"></div>
         <div id="spiral-dynamic-extent"></div>
         <div id="texture-bar-by-clipPath"></div>
-        <div id="enter-animation"></div>
+        <div id="enter-animation-and-merge"></div>
         <div id="enter-animation2"></div>
         <div id="enter-animation-clipPath"></div>
         <div id="style-animation"></div>
@@ -185,10 +185,10 @@ under the License.
                             fill: color.inner,
                             stroke: color.border
                         },
-                        during: function (elProps) {
-                            elProps.shape.points = makeShapePoints(
-                                api, valOnRadius, elProps.shape.valOnAngle
-                            );
+                        during: function (apiDuring) {
+                            apiDuring.setShape('points', makeShapePoints(
+                                api, valOnRadius, apiDuring.getShape('valOnAngle')
+                            ));
                         }
                     });
                 }
@@ -230,12 +230,12 @@ under the License.
                             verticalAlign: 'middle'
                         },
                         z2: 50,
-                        during: function (elProps) {
-                            var iValOnAngle = elProps.shape.valOnAngle;
+                        during: function (apiDuring) {
+                            var iValOnAngle = apiDuring.getShape('valOnAngle');
                             var point = makeLabelPosition(api, valOnRadius, iValOnAngle);
-                            elProps.x = point[0];
-                            elProps.y = point[1];
-                            elProps.style.text = getText(iValOnAngle);
+                            apiDuring.setAttr('x', point[0]);
+                            apiDuring.setAttr('y', point[1]);
+                            apiDuring.setStyle('text', getText(iValOnAngle));
                         }
                     });
 
@@ -414,9 +414,13 @@ under the License.
                             fill: color.inner,
                             stroke: color.border
                         },
-                        during: function (elProps) {
-                            var shp = elProps.shape;
-                            shp.points = makeShapePoints(params, shp.widthRadius, shp.startRadius, shp.endRadian);
+                        during: function (apiDuring) {
+                            apiDuring.setShape('points', makeShapePoints(
+                                params,
+                                apiDuring.getShape('widthRadius'),
+                                apiDuring.getShape('startRadius'),
+                                apiDuring.getShape('endRadian')
+                            ));
                         }
                     });
                 }
@@ -476,12 +480,17 @@ under the License.
                             }
                         },
                         z2: 50,
-                        during: function (elProps) {
-                            var shp = elProps.shape;
-                            var point = makeLabelPosition(params, shp.widthRadius, shp.startRadius, shp.endRadian);
-                            elProps.x = point[0];
-                            elProps.y = point[1];
-                            elProps.style.text = makeText(shp.endRadian);
+                        during: function (apiDuring) {
+                            var endRadian = apiDuring.getShape('endRadian');
+                            var point = makeLabelPosition(
+                                params,
+                                apiDuring.getShape('widthRadius'),
+                                apiDuring.getShape('startRadius'),
+                                endRadian
+                            );
+                            apiDuring.setAttr('x', point[0]);
+                            apiDuring.setAttr('y', point[1]);
+                            apiDuring.setStyle('text', makeText(endRadian));
                         }
                     });
 
@@ -659,8 +668,11 @@ under the License.
                                     $transition: 'polarEndRadian',
                                     $enterFrom: { polarEndRadian: 0 }
                                 },
-                                during: function (elProps) {
-                                    elProps.shape.points = makePionterPoints(params, elProps.shape.polarEndRadian);
+                                during: function (apiDuring) {
+                                    apiDuring.setShape(
+                                        'points',
+                                        makePionterPoints(params, apiDuring.getShape('polarEndRadian'))
+                                    );
                                 }
                             },
                         }, {
@@ -694,8 +706,8 @@ under the License.
                                 verticalAlign: 'middle',
                                 $enterFrom: { opacity: 0 }
                             },
-                            during: function (elProps) {
-                                elProps.style.text = makeText(elProps.shape.valOnRadian);
+                            during: function (apiDuring) {
+                                apiDuring.setStyle('text', makeText(apiDuring.getShape('valOnRadian')));
                             }
                         }]
                     };
@@ -852,11 +864,12 @@ under the License.
                     }]
                 };
 
-                var chart = testHelper.create(echarts, 'enter-animation', {
+                var chart = testHelper.create(echarts, 'enter-animation-and-merge', {
                     title: [
                         '(1) Move dataZoom, position should have transition animation.',
                         '(2) Use dataZoom hide a data item, and then show it, ensure the **fade in** animation not be interupted.',
-                        '(3) click button to setOption merge.'
+                        '(3) click button to setOption merge.',
+                        '(4) Repeat (2), should be correct.'
                     ],
                     height: 300,
                     option: option,


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