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/21 07:36:25 UTC
[echarts] branch graphic-animation updated: feat(transition) optimize enterFrom and leaveTo. fix style loose in custom
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 b30b63f feat(transition) optimize enterFrom and leaveTo. fix style loose in custom
b30b63f is described below
commit b30b63f764e4a1e88075018c1f39a288eba05d47
Author: pissang <bm...@gmail.com>
AuthorDate: Sun Nov 21 15:31:47 2021 +0800
feat(transition) optimize enterFrom and leaveTo. fix style loose in custom
---
src/animation/customGraphicTransition.ts | 239 +++++++++++++++----------------
src/chart/custom/CustomView.ts | 33 +----
src/component/graphic/GraphicView.ts | 14 +-
test/graphic-transition.html | 92 +++++++-----
4 files changed, 188 insertions(+), 190 deletions(-)
diff --git a/src/animation/customGraphicTransition.ts b/src/animation/customGraphicTransition.ts
index ef602bf..5bcba64 100644
--- a/src/animation/customGraphicTransition.ts
+++ b/src/animation/customGraphicTransition.ts
@@ -52,11 +52,14 @@ type TransformProp = keyof typeof TRANSFORM_PROPS_MAP;
const TRANSFORM_PROPS = keys(TRANSFORM_PROPS_MAP);
const transformPropNamesStr = TRANSFORM_PROPS.join(', ');
+// '' means root
+const ELEMENT_TRANSITION_PROPS = ['', 'style', 'shape', 'extra'] as const;
+
export type TransitionProps = string | string[];
export type ElementRootTransitionProp = TransformProp | 'shape' | 'extra' | 'style';
export interface TransitionOptionMixin {
- transition?: TransitionProps | 'all';
+ transition?: TransitionProps | 'all'
enterFrom?: Dictionary<unknown>;
leaveTo?: Dictionary<unknown>;
};
@@ -106,13 +109,17 @@ export interface TransitionDuringAPI<
getStyle<T extends keyof StyleOpt>(key: T): StyleOpt[T];
};
+
export function applyUpdateTransition(
el: Element,
elOption: TransitionElementOption,
animatableModel?: Model<AnimationOptionMixin>,
- dataIndex?: number,
- isInit?: boolean
+ opts?: { dataIndex?: number, isInit?: boolean, clearStyle?: boolean}
) {
+ opts = opts || {};
+ const {dataIndex, isInit, clearStyle} = opts;
+
+ const hasAnimation = animatableModel.isAnimationEnabled();
// Save the meta info for further morphing. Like apply on the sub morphing elements.
const store = transitionInnerStore(el);
const styleOpt = elOption.style;
@@ -121,24 +128,67 @@ export function applyUpdateTransition(
const transFromProps = {} as ElementProps;
const propsToSet = {} as ElementProps;
- prepareShapeOrExtraTransitionFrom('shape', el, elOption, transFromProps, isInit);
- prepareShapeOrExtraAllPropsFinal('shape', elOption, propsToSet);
-
- prepareTransformTransitionFrom(el, elOption, transFromProps, isInit);
prepareTransformAllPropsFinal(el, elOption, propsToSet);
-
- prepareShapeOrExtraTransitionFrom('extra', el, elOption, transFromProps, isInit);
+ prepareShapeOrExtraAllPropsFinal('shape', elOption, propsToSet);
prepareShapeOrExtraAllPropsFinal('extra', elOption, propsToSet);
- prepareStyleTransitionFrom(el, elOption, styleOpt, transFromProps, isInit);
+ if (!isInit && hasAnimation) {
+ prepareTransformTransitionFrom(el, elOption, transFromProps);
+ prepareShapeOrExtraTransitionFrom('shape', el, elOption, transFromProps);
+ prepareShapeOrExtraTransitionFrom('extra', el, elOption, transFromProps);
+ prepareStyleTransitionFrom(el, elOption, styleOpt, transFromProps);
+ }
+
(propsToSet as DisplayableProps).style = styleOpt;
- applyPropsDirectly(el, propsToSet);
- applyPropsTransition(el, dataIndex, animatableModel, transFromProps, isInit);
+
+ applyPropsDirectly(el, propsToSet, clearStyle);
applyMiscProps(el, elOption);
+ if (hasAnimation) {
+ if (isInit) {
+ const enterFromProps: ElementProps = {};
+ for (let i = 0; i < ELEMENT_TRANSITION_PROPS.length; i++) {
+ const propName = ELEMENT_TRANSITION_PROPS[i];
+ const prop: TransitionOptionMixin = propName ? elOption[propName] : elOption;
+ if (prop && prop.enterFrom) {
+ if (propName) {
+ (enterFromProps as any)[propName] = (enterFromProps as any)[propName] || {};
+ }
+ extend(propName ? (enterFromProps as any)[propName] : enterFromProps, prop.enterFrom);
+ }
+ }
+ initProps(el, enterFromProps, animatableModel, {
+ dataIndex: dataIndex || 0, isFrom: true
+ });
+ }
+ else {
+ applyPropsTransition(el, dataIndex || 0, animatableModel, transFromProps);
+ }
+ }
+ // Store leave to be used in leave transition.
+ updateLeaveTo(el, elOption);
+
styleOpt ? el.dirty() : el.markRedraw();
}
+export function updateLeaveTo(el: Element, elOption: TransitionElementOption) {
+ // Try merge to previous set leaveTo
+ let leaveToProps: ElementProps = transitionInnerStore(el).leaveToProps;
+ for (let i = 0; i < ELEMENT_TRANSITION_PROPS.length; i++) {
+ const propName = ELEMENT_TRANSITION_PROPS[i];
+ const prop: TransitionOptionMixin = propName ? elOption[propName] : elOption;
+ if (prop && prop.leaveTo) {
+ if (!leaveToProps) {
+ leaveToProps = transitionInnerStore(el).leaveToProps = {};
+ }
+ if (propName) {
+ (leaveToProps as any)[propName] = (leaveToProps as any)[propName] || {};
+ }
+ extend(propName ? (leaveToProps as any)[propName] : leaveToProps, prop.leaveTo);
+ }
+ }
+}
+
export function applyLeaveTransition(
el: Element,
animatableModel: Model<AnimationOptionMixin>,
@@ -166,13 +216,38 @@ export function isTransitionAll(transition: TransitionProps): transition is 'all
function applyPropsDirectly(
el: Element,
// Can be null/undefined
- allPropsFinal: ElementProps
+ allPropsFinal: ElementProps,
+ clearStyle: boolean
) {
- const elDisplayable = el.isGroup ? null : el as Displayable;
const styleOpt = (allPropsFinal as Displayable).style;
-
- if (elDisplayable && styleOpt) {
- elDisplayable.setStyle(styleOpt);
+ if (!el.isGroup && styleOpt) {
+ if (clearStyle) {
+ (el as Displayable).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 as Displayable).style);
+ }
+ }
+ }
+ (el as Displayable).setStyle(styleOpt);
}
if (allPropsFinal) {
@@ -189,8 +264,7 @@ function applyPropsTransition(
dataIndex: number,
model: Model<AnimationOptionMixin>,
// Can be null/undefined
- transFromProps: ElementProps,
- isInit: boolean
+ transFromProps: ElementProps
): void {
if (transFromProps) {
// NOTE: Do not use `el.updateDuringAnimation` here becuase `el.updateDuringAnimation` will
@@ -206,9 +280,7 @@ function applyPropsTransition(
isFrom: true,
during: cfgDuringCall
};
- isInit
- ? initProps(el, transFromProps, model, cfg)
- : updateProps(el, transFromProps, model, cfg);
+ updateProps(el, transFromProps, model, cfg);
}
}
@@ -377,9 +449,8 @@ function duringCall(
function prepareShapeOrExtraTransitionFrom(
mainAttr: 'shape' | 'extra',
fromEl: Element,
- elOption: TransitionElementOption,
- transFromProps: LooseElementProps,
- isInit: boolean
+ elOption: ElementTransitionOptionMixin,
+ transFromProps: LooseElementProps
): void {
const attrOpt: Dictionary<unknown> & TransitionOptionMixin = (elOption as any)[mainAttr];
@@ -390,21 +461,8 @@ function prepareShapeOrExtraTransitionFrom(
const elPropsInAttr = (fromEl as LooseElementProps)[mainAttr];
let transFromPropsInAttr: Dictionary<unknown>;
- const enterFrom = attrOpt.enterFrom;
- if (isInit && enterFrom) {
- !transFromPropsInAttr && (transFromPropsInAttr = transFromProps[mainAttr] = {});
- const enterFromKeys = keys(enterFrom);
- for (let i = 0; i < enterFromKeys.length; i++) {
- // `enterFrom` props are not necessarily also declared in `shape`/`style`/...,
- // for example, `opacity` can only declared in `enterFrom` but not in `style`.
- const key = enterFromKeys[i];
- // Do not clone, animator will perform that clone.
- transFromPropsInAttr[key] = enterFrom[key];
- }
- }
-
- if (!isInit && elPropsInAttr) {
+ if (elPropsInAttr) {
const transition = elOption.transition;
const attrTransition = attrOpt.transition;
if (attrTransition) {
@@ -433,17 +491,6 @@ function prepareShapeOrExtraTransitionFrom(
}
}
}
-
- const leaveTo = attrOpt.leaveTo;
- if (leaveTo) {
- const leaveToProps = getOrCreateLeaveToPropsFromEl(fromEl);
- const leaveToPropsInAttr: Dictionary<unknown> = leaveToProps[mainAttr] || (leaveToProps[mainAttr] = {});
- const leaveToKeys = keys(leaveTo);
- for (let i = 0; i < leaveToKeys.length; i++) {
- const key = leaveToKeys[i];
- leaveToPropsInAttr[key] = leaveTo[key];
- }
- }
}
function prepareShapeOrExtraAllPropsFinal(
@@ -468,52 +515,23 @@ function prepareShapeOrExtraAllPropsFinal(
function prepareTransformTransitionFrom(
el: Element,
elOption: TransitionElementOption,
- transFromProps: ElementProps,
- isInit: boolean
+ transFromProps: ElementProps
): void {
- const enterFrom = elOption.enterFrom;
- if (isInit && enterFrom) {
- const enterFromKeys = keys(enterFrom);
- for (let i = 0; i < enterFromKeys.length; i++) {
- const key = enterFromKeys[i] as TransformProp;
- if (__DEV__) {
- checkTransformPropRefer(key, 'el.enterFrom');
- }
- // Do not clone, animator will perform that clone.
- transFromProps[key] = enterFrom[key] as number;
+ const transition = elOption.transition;
+ const transitionKeys = isTransitionAll(transition)
+ ? TRANSFORM_PROPS
+ : normalizeToArray(transition || []);
+ for (let i = 0; i < transitionKeys.length; i++) {
+ const key = transitionKeys[i];
+ if (key === 'style' || key === 'shape' || key === 'extra') {
+ continue;
}
- }
-
- if (!isInit) {
- const transition = elOption.transition;
- const transitionKeys = isTransitionAll(transition)
- ? TRANSFORM_PROPS
- : normalizeToArray(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 leaveTo = elOption.leaveTo;
- if (leaveTo) {
- const leaveToProps = getOrCreateLeaveToPropsFromEl(el);
- const leaveToKeys = keys(leaveTo);
- for (let i = 0; i < leaveToKeys.length; i++) {
- const key = leaveToKeys[i] as TransformProp;
- if (__DEV__) {
- checkTransformPropRefer(key, 'el.leaveTo');
- }
- leaveToProps[key] = leaveTo[key] as number;
+ const elVal = el[key];
+ if (__DEV__) {
+ checkTransformPropRefer(key, 'el.transition');
}
+ // Do not clone, animator will perform that clone.
+ transFromProps[key] = elVal;
}
}
@@ -544,8 +562,7 @@ function prepareStyleTransitionFrom(
fromEl: Element,
elOption: TransitionElementOption,
styleOpt: TransitionElementOption['style'],
- transFromProps: LooseElementProps,
- isInit: boolean
+ transFromProps: LooseElementProps
): void {
if (!styleOpt) {
return;
@@ -554,18 +571,7 @@ function prepareStyleTransitionFrom(
const fromElStyle = (fromEl as LooseElementProps).style as LooseElementProps['style'];
let transFromStyleProps: LooseElementProps['style'];
- const enterFrom = styleOpt.enterFrom;
- if (isInit && enterFrom) {
- const enterFromKeys = keys(enterFrom);
- !transFromStyleProps && (transFromStyleProps = transFromProps.style = {});
- for (let i = 0; i < enterFromKeys.length; i++) {
- const key = enterFromKeys[i];
- // Do not clone, animator will perform that clone.
- (transFromStyleProps as any)[key] = enterFrom[key];
- }
- }
-
- if (!isInit && fromElStyle) {
+ if (fromElStyle) {
const styleTransition = styleOpt.transition;
const elTransition = elOption.transition;
if (styleTransition && !isTransitionAll(styleTransition)) {
@@ -601,17 +607,6 @@ function prepareStyleTransitionFrom(
}
}
}
-
- const leaveTo = styleOpt.leaveTo;
- if (leaveTo) {
- const leaveToKeys = keys(leaveTo);
- const leaveToProps = getOrCreateLeaveToPropsFromEl(fromEl);
- const leaveToStyleProps = leaveToProps.style || (leaveToProps.style = {});
- for (let i = 0; i < leaveToKeys.length; i++) {
- const key = leaveToKeys[i];
- (leaveToStyleProps as any)[key] = leaveTo[key];
- }
- }
}
function isNonStyleTransitionEnabled(optVal: unknown, elVal: unknown): boolean {
@@ -629,10 +624,4 @@ if (__DEV__) {
+ 'Only `' + keys(TRANSFORM_PROPS_MAP).join('`, `') + '` are permitted.');
}
};
-}
-
-function getOrCreateLeaveToPropsFromEl(el: Element): LooseElementProps {
- const innerEl = transitionInnerStore(el);
- return innerEl.leaveToProps || (innerEl.leaveToProps = {});
-}
-
+}
\ No newline at end of file
diff --git a/src/chart/custom/CustomView.ts b/src/chart/custom/CustomView.ts
index 0024c82..cac33f7 100644
--- a/src/chart/custom/CustomView.ts
+++ b/src/chart/custom/CustomView.ts
@@ -485,36 +485,13 @@ function updateElNormal(
(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);
- }
- }
}
- applyUpdateTransition(el, elOption, seriesModel, dataIndex, isInit);
-
+ applyUpdateTransition(el, elOption, seriesModel, {
+ dataIndex,
+ isInit,
+ clearStyle: true
+ });
if (!isTextContent) {
// `elOption.info` enables user to mount some info on
diff --git a/src/component/graphic/GraphicView.ts b/src/component/graphic/GraphicView.ts
index 9d90fe0..6b8d25f 100644
--- a/src/component/graphic/GraphicView.ts
+++ b/src/component/graphic/GraphicView.ts
@@ -38,7 +38,12 @@ import {
GraphicComponentGroupOption,
GraphicComponentElementOption
} from './GraphicModel';
-import { applyLeaveTransition, applyUpdateTransition, isTransitionAll } from '../../animation/customGraphicTransition';
+import {
+ applyLeaveTransition,
+ applyUpdateTransition,
+ isTransitionAll,
+ updateLeaveTo
+} from '../../animation/customGraphicTransition';
import { updateProps } from '../../animation/basicTrasition';
const nonShapeGraphicElements = {
@@ -172,8 +177,7 @@ export class GraphicComponentView extends ComponentView {
el,
elOptionCleaned,
graphicModel,
- 0, // TODO Fixed dataIndex to be 0
- isInit
+ { isInit }
);
updateZ(el, elOption, globalZ, globalZLevel);
}
@@ -186,13 +190,13 @@ export class GraphicComponentView extends ComponentView {
el,
elOptionCleaned,
graphicModel,
- 0, // TODO Fixed dataIndex to be 0
- true
+ { isInit: true}
);
updateZ(el, elOption, globalZ, globalZLevel);
}
}
else if ($action === 'remove') {
+ updateLeaveTo(elExisting, elOption);
removeEl(elExisting, elMap, graphicModel);
}
diff --git a/test/graphic-transition.html b/test/graphic-transition.html
index 7b2cf01..497e352 100644
--- a/test/graphic-transition.html
+++ b/test/graphic-transition.html
@@ -39,6 +39,7 @@ under the License.
<div id="main0"></div>
<div id="main1"></div>
+ <div id="main2"></div>
@@ -184,10 +185,10 @@ under the License.
chart.setOption({
graphic: {
elements: [{
- x: Math.random() * chart.getWidth(),
- y: Math.random() * chart.getHeight(),
+ x: Math.random() * (chart.getWidth() - 100) + 50,
+ y: Math.random() * (chart.getHeight() - 100) + 50,
shape: {
- r: Math.random() * 30 + 50
+ r: Math.random() * 40 + 10
},
style: {
fill: echarts.color.random()
@@ -210,47 +211,74 @@ under the License.
<script>
require(['echarts'/*, 'map/js/china' */], function (echarts) {
+
+ var elements = [{
+ type: 'circle',
+ name: 'target',
+ x: 300,
+ y: 100,
+ shape: {
+ cx: 0,
+ cy: 0,
+ r: 100
+ },
+ style: {
+ fill: 'gray'
+ }
+ }]
var option = {
graphic: {
- elements: [{
- type: 'circle',
- x: 200,
- y: 200,
- shape: {
- cx: 0,
- cy: 0,
- r: 100
- },
- style: {
- fill: 'gray'
- }
- }]
+ elements: elements
}
}
- var chart = testHelper.create(echarts, 'main1', {
+ var chart = testHelper.create(echarts, 'main2', {
title: [
- 'Transition all'
+ 'Enter transition'
],
option: option,
buttons: [
{
- text: 'Randomize',
+ text: 'Create',
onclick() {
- chart.setOption({
- graphic: {
- elements: [{
- x: Math.random() * chart.getWidth(),
- y: Math.random() * chart.getHeight(),
- shape: {
- r: Math.random() * 30 + 50
- },
- style: {
- fill: echarts.color.random()
- }
- }]
+ elements[1] = {
+ type: 'circle',
+ name: 'center',
+ x: 300,
+ y: 100,
+ enterFrom: {
+ x: 0,
+ y: 100,
+ shape: {
+ r: 0
+ }
+ },
+ shape: {
+ cx: 0,
+ cy: 0,
+ r: 80
+ },
+ style: {
+ fill: 'orange'
}
- })
+ };
+ chart.setOption(option)
+ }
+ },
+ {
+ text: 'Remove',
+ onclick() {
+ elements[1] = {
+ leaveTo: {
+ x: 500,
+ y: 100,
+ shape: {
+ r: 0
+ }
+ },
+ $action: 'remove'
+ };
+ chart.setOption(option)
}
},
]
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org