You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by sh...@apache.org on 2021/11/19 06:28:06 UTC

[echarts] branch graphic-animation updated: feat(transition): support transition to be configured all.

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

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


The following commit(s) were added to refs/heads/graphic-animation by this push:
     new a3eedb3  feat(transition): support transition to be configured all.
a3eedb3 is described below

commit a3eedb3b29fc1527313e14583ef63999c93cdf00
Author: pissang <bm...@gmail.com>
AuthorDate: Fri Nov 19 14:27:00 2021 +0800

    feat(transition): support transition to be configured all.
---
 src/animation/customGraphicTransition.ts | 107 +++++++++++--------------------
 src/chart/custom/CustomView.ts           |  48 ++++++++++++--
 src/component/graphic/GraphicModel.ts    |  33 ++++++++++
 test/custom-transition2.html             |   4 +-
 test/graphic-transition.html             |  68 ++++++++++++++++++--
 5 files changed, 175 insertions(+), 85 deletions(-)

diff --git a/src/animation/customGraphicTransition.ts b/src/animation/customGraphicTransition.ts
index 7459f53..90d7dfa 100644
--- a/src/animation/customGraphicTransition.ts
+++ b/src/animation/customGraphicTransition.ts
@@ -18,12 +18,10 @@
 */
 
 // Helpers for custom graphic elements in custom series and graphic components.
-
-import Transformable from 'zrender/src/core/Transformable';
 import Element, { ElementProps } from 'zrender/src/Element';
 
 import { makeInner, normalizeToArray } from '../util/model';
-import { assert, bind, eqNaN, hasOwn, indexOf, isArrayLike, keys } from 'zrender/src/core/util';
+import { assert, bind, eqNaN, extend, hasOwn, indexOf, isArrayLike, keys } from 'zrender/src/core/util';
 import { cloneValue } from 'zrender/src/animation/Animator';
 import Displayable, { DisplayableProps } from 'zrender/src/graphic/Displayable';
 import Model from '../model/Model';
@@ -40,7 +38,6 @@ const LEGACY_TRANSFORM_PROPS_MAP = {
     origin: ['originX', 'originY']
 } as const;
 const LEGACY_TRANSFORM_PROPS = keys(LEGACY_TRANSFORM_PROPS_MAP);
-type LegacyTransformProp = keyof typeof LEGACY_TRANSFORM_PROPS_MAP;
 
 const TRANSFORM_PROPS_MAP = {
     x: 1,
@@ -55,19 +52,20 @@ type TransformProp = keyof typeof TRANSFORM_PROPS_MAP;
 const TRANSFORM_PROPS = keys(TRANSFORM_PROPS_MAP);
 const transformPropNamesStr = TRANSFORM_PROPS.join(', ');
 
-export type CustomTransitionProps = string | string[];
+export type TransitionProps = string | string[];
+export type ElementRootTransitionProp = TransformProp | 'shape' | 'extra' | 'style';
+
 export interface TransitionOptionMixin {
-    transition?: CustomTransitionProps;
+    transition?: TransitionProps | 'all';
     enterFrom?: Dictionary<unknown>;
     leaveTo?: Dictionary<unknown>;
 };
 
 export type ElementTransitionOptionMixin = {
-    transition?: ElementRootTransitionProp | ElementRootTransitionProp[];
+    transition?: ElementRootTransitionProp | ElementRootTransitionProp[] | 'all';
     enterFrom?: Dictionary<number>;
     leaveTo?: Dictionary<number>;
 };
-type ElementRootTransitionProp = TransformProp | 'shape' | 'extra' | 'style';
 
 interface LooseElementProps extends ElementProps {
     style?: ZRStyleProps;
@@ -91,18 +89,6 @@ const transitionInnerStore = makeInner<{
     userDuring: (params: TransitionDuringAPI) => void;
 }, Element>();
 
-
-function setTransformPropToTransitionFrom(
-    transitionFrom: Partial<Pick<Transformable, TransformProp>>,
-    name: TransformProp,
-    fromTransformable?: Transformable // If provided, retrieve from the element.
-): void {
-    if (fromTransformable) {
-        transitionFrom[name] = fromTransformable[name];
-    }
-}
-
-
 export interface TransitionBaseDuringAPI {
     // Usually other props do not need to be changed in animation during.
     setTransform(key: TransformProp, val: number): this
@@ -182,32 +168,7 @@ function applyPropsDirectly(
     const styleOpt = (allPropsFinal as Displayable).style;
 
     if (elDisplayable && styleOpt) {
-
-        // PENDING: here the input style object is used directly.
-        // Good for performance but bad for compatibility control.
-        elDisplayable.useStyle(styleOpt);
-        // When style object changed, how to trade the existing animation?
-        // It is probably complicated and not needed to cover all the cases.
-        // But still need consider the case:
-        // (1) When using init animation on `style.opacity`, and before the animation
-        //     ended users triggers an update by mousewhel. At that time the init
-        //     animation should better be continued rather than terminated.
-        //     So after `useStyle` called, we should change the animation target manually
-        //     to continue the effect of the init animation.
-        // (2) PENDING: If the previous animation targeted at a `val1`, and currently we need
-        //     to update the value to `val2` and no animation declared, should be terminate
-        //     the previous animation or just modify the target of the animation?
-        //     Therotically That will happen not only on `style` but also on `shape` and
-        //     `transfrom` props. But we haven't handle this case at present yet.
-        // (3) PENDING: Is it proper to visit `animators` and `targetName`?
-        const animators = elDisplayable.animators;
-        for (let i = 0; i < animators.length; i++) {
-            const animator = animators[i];
-            // targetName is the "topKey".
-            if (animator.targetName === 'style') {
-                animator.changeTarget(elDisplayable.style);
-            }
-        }
+        elDisplayable.setStyle(styleOpt);
     }
 
     if (allPropsFinal) {
@@ -438,17 +399,25 @@ function prepareShapeOrExtraTransitionFrom(
         }
     }
 
+
     if (!isInit && elPropsInAttr) {
-        if (attrOpt.transition) {
+        const transition = elOption.transition;
+        const attrTransition = attrOpt.transition;
+        if (attrTransition) {
             !transFromPropsInAttr && (transFromPropsInAttr = transFromProps[mainAttr] = {});
-            const transitionKeys = normalizeToArray(attrOpt.transition);
-            for (let i = 0; i < transitionKeys.length; i++) {
-                const key = transitionKeys[i];
-                const elVal = elPropsInAttr[key];
-                transFromPropsInAttr[key] = elVal;
+            if (attrTransition === 'all') {
+                extend(transFromPropsInAttr, elPropsInAttr);
+            }
+            else {
+                const transitionKeys = normalizeToArray(attrTransition);
+                for (let i = 0; i < transitionKeys.length; i++) {
+                    const key = transitionKeys[i];
+                    const elVal = elPropsInAttr[key];
+                    transFromPropsInAttr[key] = elVal;
+                }
             }
         }
-        else if (indexOf(elOption.transition, mainAttr) >= 0) {
+        else if (transition === 'all' || indexOf(transition, mainAttr) >= 0) {
             !transFromPropsInAttr && (transFromPropsInAttr = transFromProps[mainAttr] = {});
             const elPropsInAttrKeys = keys(elPropsInAttr);
             for (let i = 0; i < elPropsInAttrKeys.length; i++) {
@@ -512,25 +481,21 @@ function prepareTransformTransitionFrom(
     }
 
     if (!isInit) {
-        if (elOption.transition) {
-            const transitionKeys = normalizeToArray(elOption.transition);
-            for (let i = 0; i < transitionKeys.length; i++) {
-                const key = transitionKeys[i];
-                if (key === 'style' || key === 'shape' || key === 'extra') {
-                    continue;
-                }
-                const elVal = el[key];
-                if (__DEV__) {
-                    checkTransformPropRefer(key, 'el.transition');
-                }
-                // Do not clone, animator will perform that clone.
-                transFromProps[key] = elVal;
+        const transition = elOption.transition;
+        const transitionKeys = transition === 'all'
+            ? TRANSFORM_PROPS
+            : normalizeToArray(transition || []);
+        for (let i = 0; i < transitionKeys.length; i++) {
+            const key = transitionKeys[i];
+            if (key === 'style' || key === 'shape' || key === 'extra') {
+                continue;
             }
-        }
-        // This default transition see [STRATEGY_TRANSITION]
-        else {
-            setTransformPropToTransitionFrom(transFromProps, 'x', el);
-            setTransformPropToTransitionFrom(transFromProps, 'y', el);
+            const elVal = el[key];
+            if (__DEV__) {
+                checkTransformPropRefer(key, 'el.transition');
+            }
+            // Do not clone, animator will perform that clone.
+            transFromProps[key] = elVal;
         }
     }
 
diff --git a/src/chart/custom/CustomView.ts b/src/chart/custom/CustomView.ts
index 47f90f1..0024c82 100644
--- a/src/chart/custom/CustomView.ts
+++ b/src/chart/custom/CustomView.ts
@@ -90,7 +90,11 @@ import CustomSeriesModel, {
 } from './CustomSeries';
 import { PatternObject } from 'zrender/src/graphic/Pattern';
 import { CustomSeriesOption } from '../../export/option';
-import { applyLeaveTransition, applyUpdateTransition } from '../../animation/customGraphicTransition';
+import {
+    applyLeaveTransition,
+    applyUpdateTransition,
+    ElementRootTransitionProp
+} from '../../animation/customGraphicTransition';
 
 const EMPHASIS = 'emphasis' as const;
 const NORMAL = 'normal' as const;
@@ -109,6 +113,7 @@ const PATH_LABEL = {
     blur: [BLUR, 'label'],
     select: [SELECT, 'label']
 } as const;
+const DEFAULT_TRANSITION: ElementRootTransitionProp[] = ['x', 'y'];
 // Use prefix to avoid index to be the same as el.name,
 // which will cause weird update animation.
 const GROUP_DIFF_PREFIX = 'e\0\0';
@@ -443,6 +448,11 @@ function updateElNormal(
         el.setTextConfig(txCfgOpt);
     }
 
+    // Default transition ['x', 'y']
+    if (elOption && elOption.transition == null) {
+        elOption.transition = DEFAULT_TRANSITION;
+    }
+
     // Do some normalization on style.
     const styleOpt = elOption && (elOption as CustomDisplayableOption).style;
 
@@ -468,10 +478,38 @@ function updateElNormal(
         (styleOpt as InnerCustomZRPathOptionStyle).__decalPattern = decalPattern;
     }
 
-    if (isDisplayable(el) && styleOpt) {
-        const decalPattern = (styleOpt as InnerCustomZRPathOptionStyle).__decalPattern;
-        if (decalPattern) {
-            (styleOpt as PathStyleProps).decal = decalPattern;
+    if (isDisplayable(el)) {
+        if (styleOpt) {
+            const decalPattern = (styleOpt as InnerCustomZRPathOptionStyle).__decalPattern;
+            if (decalPattern) {
+                (styleOpt as PathStyleProps).decal = decalPattern;
+            }
+        }
+
+        // Clear style
+        el.useStyle({});
+
+        // When style object changed, how to trade the existing animation?
+        // It is probably complicated and not needed to cover all the cases.
+        // But still need consider the case:
+        // (1) When using init animation on `style.opacity`, and before the animation
+        //     ended users triggers an update by mousewhel. At that time the init
+        //     animation should better be continued rather than terminated.
+        //     So after `useStyle` called, we should change the animation target manually
+        //     to continue the effect of the init animation.
+        // (2) PENDING: If the previous animation targeted at a `val1`, and currently we need
+        //     to update the value to `val2` and no animation declared, should be terminate
+        //     the previous animation or just modify the target of the animation?
+        //     Therotically That will happen not only on `style` but also on `shape` and
+        //     `transfrom` props. But we haven't handle this case at present yet.
+        // (3) PENDING: Is it proper to visit `animators` and `targetName`?
+        const animators = el.animators;
+        for (let i = 0; i < animators.length; i++) {
+            const animator = animators[i];
+            // targetName is the "topKey".
+            if (animator.targetName === 'style') {
+                animator.changeTarget(el.style);
+            }
         }
     }
 
diff --git a/src/component/graphic/GraphicModel.ts b/src/component/graphic/GraphicModel.ts
index 8c49869..2a64d74 100644
--- a/src/component/graphic/GraphicModel.ts
+++ b/src/component/graphic/GraphicModel.ts
@@ -210,6 +210,7 @@ export function setKeyInfoToNewElOption(
     newElOption.parentOption = null;
 }
 
+const TRANSITION_PROPS = ['transition', 'enterFrom', 'leaveTo'] as const;
 function isSetLoc(
     obj: GraphicComponentElementOption,
     props: ('left' | 'right' | 'top' | 'bottom')[]
@@ -247,6 +248,13 @@ function mergeNewElOptionToExist(
             mergeLayoutParam(existElOption, newElOptCopy, { ignoreSize: true });
             // Will be used in render.
             copyLayoutParams(newElOption, existElOption);
+
+            // Copy transition info to new option so it can be used in the transition.
+            // DO IT AFTER merge
+            copyTransitionInfo(newElOption, existElOption);
+            copyTransitionInfo(newElOption, existElOption, 'shape');
+            copyTransitionInfo(newElOption, existElOption, 'style');
+            copyTransitionInfo(newElOption, existElOption, 'extra');
         }
         else {
             existList[index] = newElOptCopy;
@@ -261,6 +269,31 @@ function mergeNewElOptionToExist(
     }
 }
 
+function copyTransitionInfo(
+    target: GraphicComponentElementOption, source: GraphicComponentElementOption, targetProp?: string
+) {
+    if (targetProp) {
+        if (!(target as any)[targetProp]
+            && (source as any)[targetProp]
+        ) {
+            // TODO avoid creating this empty object when there is no transition configuration.
+            (target as any)[targetProp] = {};
+        }
+        target = (target as any)[targetProp];
+        source = (source as any)[targetProp];
+    }
+    if (!target || !source) {
+        return;
+    }
+
+    for (let i = 0; i < TRANSITION_PROPS.length; i++) {
+        const prop = TRANSITION_PROPS[i];
+        if (target[prop] == null && source[prop] != null) {
+            (target as any)[prop] = source[prop];
+        }
+    }
+}
+
 function setLayoutInfoToExist(
     existItem: GraphicComponentElementOption,
     newElOption: GraphicComponentElementOption
diff --git a/test/custom-transition2.html b/test/custom-transition2.html
index 7c03dad..e28095d 100644
--- a/test/custom-transition2.html
+++ b/test/custom-transition2.html
@@ -214,7 +214,7 @@ under the License.
                 var textOpt = {
                     type: 'text',
                     extra: { },
-                    transition: [], // disable the default transition of x y.
+                    // transition: [], // disable the default transition of x y.
                     style: { x: 20, y: 20, fontSize: 20, stroke: 'green' },
                     during: function (apiDuring) {
                         var x = apiDuring.getExtra('x');
@@ -227,7 +227,7 @@ under the License.
                     shape: { cx: 0, cy: 0, r: 10 },
                     extra: { },
                     style: { fill: 'red' },
-                    transition: [], // disable the default transition of x y.
+                    // transition: [], // disable the default transition of x y.
                     during: function (apiDuring) {
                         var x = apiDuring.getExtra('x');
                         var y = apiDuring.getExtra('y');
diff --git a/test/graphic-transition.html b/test/graphic-transition.html
index b4c8de9..9d80be3 100644
--- a/test/graphic-transition.html
+++ b/test/graphic-transition.html
@@ -51,6 +51,7 @@ under the License.
                         type: 'circle',
                         x: 100,
                         y: 50,
+                        transition: ['x', 'y'],
                         shape: {
                             cx: 0,
                             cy: 0,
@@ -65,7 +66,7 @@ under the License.
 
             var chart = testHelper.create(echarts, 'main0', {
                 title: [
-                    'Basic transition'
+                    'Transform transition'
                 ],
                 option: option,
                 buttons: [
@@ -75,8 +76,6 @@ under the License.
                             chart.setOption({
                                 graphic: {
                                     elements: [{
-                                        type: 'circle',
-                                        transition: ['x', 'y'],
                                         x: 200
                                     }]
                                 }
@@ -91,8 +90,6 @@ under the License.
                             chart.setOption({
                                 graphic: {
                                     elements: [{
-                                        type: 'circle',
-                                        transition: ['x', 'y'],
                                         y: 200
                                     }]
                                 }
@@ -106,14 +103,26 @@ under the License.
                             chart.setOption({
                                 graphic: {
                                     elements: [{
-                                        type: 'circle',
-                                        transition: ['x', 'y'],
                                         x: 100,
                                         y: 50
                                     }]
                                 }
                             })
                         }
+                    },
+
+                    {
+                        text: 'Move to center',
+                        onclick() {
+                            chart.setOption({
+                                graphic: {
+                                    elements: [{
+                                        left: 'center',
+                                        top: 'center'
+                                    }]
+                                }
+                            })
+                        }
                     }
                 ]
             });
@@ -122,6 +131,51 @@ under the License.
         </script>
 
 
+        <script>
+        require(['echarts'/*, 'map/js/china' */], function (echarts) {
+            var option = {
+                graphic: {
+                    elements: [{
+                        type: 'circle',
+                        x: 100,
+                        y: 50,
+                        transition: ['x', 'y'],
+                        shape: {
+                            cx: 0,
+                            cy: 0,
+                            r: 50
+                        },
+                        style: {
+                            fill: 'orange'
+                        }
+                    }]
+                }
+            }
+
+            var chart = testHelper.create(echarts, 'main1', {
+                title: [
+                    'Transition all'
+                ],
+                option: option,
+                buttons: [
+                    {
+                        text: 'Randomize',
+                        onclick() {
+                            chart.setOption({
+                                graphic: {
+                                    elements: [{
+                                        x: 200
+                                    }]
+                                }
+                            })
+                        }
+                    },
+                ]
+            });
+
+        });
+        </script>
+
 
 
 

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