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