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/24 15:41:47 UTC
[echarts] branch graphic-animation updated: feat(transition): add animation config per element
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 393eb30 feat(transition): add animation config per element
393eb30 is described below
commit 393eb307cfb63e983e4ed9939e0820a835311df9
Author: pissang <bm...@gmail.com>
AuthorDate: Wed Nov 24 23:36:51 2021 +0800
feat(transition): add animation config per element
---
src/animation/basicTrasition.ts | 14 +++----
src/animation/customGraphicTransition.ts | 68 +++++++++++++++++++++-----------
src/chart/custom/CustomSeries.ts | 5 +++
src/chart/custom/CustomView.ts | 12 +++---
src/chart/helper/Symbol.ts | 4 +-
src/component/graphic/GraphicModel.ts | 23 +++++++----
src/component/graphic/GraphicView.ts | 27 +++++++------
7 files changed, 97 insertions(+), 56 deletions(-)
diff --git a/src/animation/basicTrasition.ts b/src/animation/basicTrasition.ts
index bc31f39..3863b6b 100644
--- a/src/animation/basicTrasition.ts
+++ b/src/animation/basicTrasition.ts
@@ -55,7 +55,7 @@ type AnimateOrSetPropsOption = {
* Return null if animation is disabled.
*/
export function getAnimationConfig(
- animationType: 'init' | 'update' | 'remove',
+ animationType: 'enter' | 'update' | 'leave',
animatableModel: Model<AnimationOptionMixin>,
dataIndex: number,
// Extra opts can override the option in animatable model.
@@ -124,7 +124,7 @@ export function getAnimationConfig(
}
function animateOrSetProps<Props>(
- animationType: 'init' | 'update' | 'remove',
+ animationType: 'enter' | 'update' | 'leave',
el: Element<Props>,
props: Props,
animatableModel?: Model<AnimationOptionMixin> & {
@@ -149,11 +149,11 @@ function animateOrSetProps<Props>(
dataIndex = dataIndex.dataIndex;
}
- const isRemove = (animationType === 'remove');
+ const isRemove = (animationType === 'leave');
if (!isRemove) {
// Must stop the remove animation.
- el.stopAnimation('remove');
+ el.stopAnimation('leave');
}
const animationConfig = getAnimationConfig(
@@ -245,7 +245,7 @@ export function initProps<Props>(
cb?: AnimateOrSetPropsOption['cb'] | AnimateOrSetPropsOption['during'],
during?: AnimateOrSetPropsOption['during']
) {
- animateOrSetProps('init', el, props, animatableModel, dataIndex, cb, during);
+ animateOrSetProps('enter', el, props, animatableModel, dataIndex, cb, during);
}
/**
@@ -258,7 +258,7 @@ export function initProps<Props>(
}
for (let i = 0; i < el.animators.length; i++) {
const animator = el.animators[i];
- if (animator.scope === 'remove') {
+ if (animator.scope === 'leave') {
return true;
}
}
@@ -281,7 +281,7 @@ export function removeElement<Props>(
return;
}
- animateOrSetProps('remove', el, props, animatableModel, dataIndex, cb, during);
+ animateOrSetProps('leave', el, props, animatableModel, dataIndex, cb, during);
}
function fadeOutDisplayable(
diff --git a/src/animation/customGraphicTransition.ts b/src/animation/customGraphicTransition.ts
index 543112b..e09772f 100644
--- a/src/animation/customGraphicTransition.ts
+++ b/src/animation/customGraphicTransition.ts
@@ -18,14 +18,14 @@
*/
// Helpers for creating transitions in custom series and graphic components.
-import Element, { ElementProps } from 'zrender/src/Element';
+import Element, { ElementAnimateConfig, ElementProps } from 'zrender/src/Element';
import { makeInner, normalizeToArray } from '../util/model';
import { assert, bind, each, 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';
-import { initProps, updateProps } from './basicTrasition';
+import { getAnimationConfig, updateProps } from './basicTrasition';
import { Path } from '../util/graphic';
import { warn } from '../util/log';
import { AnimationOption, AnimationOptionMixin, ZRStyleProps } from '../util/types';
@@ -58,8 +58,8 @@ export const ELEMENT_ANIMATABLE_PROPS = ['', 'style', 'shape', 'extra'] as const
export type TransitionProps = string | string[];
export type ElementRootTransitionProp = TransformProp | 'shape' | 'extra' | 'style';
-export interface TransitionOptionMixin<T = unknown> {
- transition?: TransitionProps | 'all'
+export interface TransitionOptionMixin<T = Record<string, any>> {
+ transition?: (keyof T & string) | ((keyof T & string)[]) | 'all'
enterFrom?: T;
leaveTo?: T;
@@ -108,6 +108,20 @@ export interface TransitionDuringAPI<
getStyle<T extends keyof StyleOpt>(key: T): StyleOpt[T];
};
+function getElementAnimationConfig(
+ animationType: 'enter' | 'update' | 'leave',
+ elOption: TransitionElementOption,
+ parentModel: Model<AnimationOptionMixin>,
+ dataIndex?: number
+) {
+ const animationProp = `${animationType}Animation` as const;
+ const config: ElementAnimateConfig = getAnimationConfig(animationType, parentModel, dataIndex) || {};
+ config.setToFinal = true;
+ config.scope = animationType;
+ extend(config, elOption[animationProp]);
+ return config;
+}
+
export function applyUpdateTransition(
el: Element,
@@ -155,12 +169,13 @@ export function applyUpdateTransition(
extend(propName ? (enterFromProps as any)[propName] : enterFromProps, prop.enterFrom);
}
});
- initProps(el, enterFromProps, animatableModel, {
- dataIndex: dataIndex || 0, isFrom: true
- });
+ const config = getElementAnimationConfig('enter', elOption, animatableModel, dataIndex);
+ if (config.duration > 0) {
+ el.animateFrom(enterFromProps, config);
+ }
}
else {
- applyPropsTransition(el, dataIndex || 0, animatableModel, transFromProps);
+ applyPropsTransition(el, elOption, dataIndex || 0, animatableModel, transFromProps);
}
}
// Store leave to be used in leave transition.
@@ -189,20 +204,27 @@ export function updateLeaveTo(el: Element, elOption: TransitionElementOption) {
export function applyLeaveTransition(
el: Element,
+ elOption: TransitionElementOption,
animatableModel: Model<AnimationOptionMixin>,
onRemove?: () => void
): void {
if (el) {
const parent = el.parent;
const leaveToProps = transitionInnerStore(el).leaveToProps;
- leaveToProps
- ? updateProps(el, leaveToProps, animatableModel, {
- cb: function () {
- parent.remove(el);
- onRemove && onRemove();
- }
- })
- : (parent.remove(el), onRemove && onRemove());
+ if (leaveToProps) {
+ // TODO TODO use leave after leaveAnimation in series is introduced
+ // TODO Data index?
+ const config = getElementAnimationConfig('update', elOption, animatableModel, 0);
+ config.done = () => {
+ parent.remove(el);
+ onRemove && onRemove();
+ };
+ el.animateTo(leaveToProps, config);
+ }
+ else {
+ parent.remove(el);
+ onRemove && onRemove();
+ }
}
}
@@ -259,6 +281,7 @@ function applyPropsDirectly(
function applyPropsTransition(
el: Element,
+ elOption: TransitionElementOption,
dataIndex: number,
model: Model<AnimationOptionMixin>,
// Can be null/undefined
@@ -273,12 +296,12 @@ function applyPropsTransition(
const userDuring = transitionInnerStore(el).userDuring;
// For simplicity, if during not specified, the previous during will not work any more.
const cfgDuringCall = userDuring ? bind(duringCall, { el: el, userDuring: userDuring }) : null;
- const cfg = {
- dataIndex: dataIndex,
- isFrom: true,
- during: cfgDuringCall
- };
- updateProps(el, transFromProps, model, cfg);
+
+ const config = getElementAnimationConfig('update', elOption, model, dataIndex);
+ if (config.duration > 0) {
+ config.during = cfgDuringCall;
+ el.animateFrom(transFromProps, config);
+ }
}
}
@@ -447,7 +470,6 @@ function prepareShapeOrExtraTransitionFrom(
const elPropsInAttr = (fromEl as LooseElementProps)[mainAttr];
let transFromPropsInAttr: Dictionary<unknown>;
-
if (elPropsInAttr) {
const transition = elOption.transition;
const attrTransition = attrOpt.transition;
diff --git a/src/chart/custom/CustomSeries.ts b/src/chart/custom/CustomSeries.ts
index a415574..5a8e998 100644
--- a/src/chart/custom/CustomSeries.ts
+++ b/src/chart/custom/CustomSeries.ts
@@ -23,6 +23,7 @@ import { PathProps, PathStyleProps } from 'zrender/src/graphic/Path';
import { ZRenderType } from 'zrender/src/zrender';
import { BarGridLayoutOptionForCustomSeries, BarGridLayoutResult } from '../../layout/barGrid';
import {
+ AnimationOption,
BlurScope,
CallbackDataParams,
Dictionary,
@@ -119,6 +120,10 @@ export interface CustomBaseElementOption extends Partial<Pick<
extra?: Dictionary<unknown> & TransitionOptionMixin;
// updateDuringAnimation
during?(params: TransitionBaseDuringAPI): void;
+
+ enterAnimation?: AnimationOption
+ updateAnimation?: AnimationOption
+ leaveAnimation?: AnimationOption
};
export interface CustomDisplayableOption extends CustomBaseElementOption, Partial<Pick<
diff --git a/src/chart/custom/CustomView.ts b/src/chart/custom/CustomView.ts
index cac33f7..e98b11c 100644
--- a/src/chart/custom/CustomView.ts
+++ b/src/chart/custom/CustomView.ts
@@ -223,7 +223,8 @@ export default class CustomChartView extends ChartView {
);
})
.remove(function (oldIdx) {
- applyLeaveTransition(oldData.getItemGraphicEl(oldIdx), customSeries);
+ const el = oldData.getItemGraphicEl(oldIdx);
+ applyLeaveTransition(el, customInnerStore(el).option, customSeries);
})
.update(function (newIdx, oldIdx) {
const oldEl = oldData.getItemGraphicEl(oldIdx);
@@ -1059,8 +1060,8 @@ function doesElNeedRecreate(el: Element, elOption: CustomElementOption, seriesMo
&& elOptionType !== elInner.customGraphicType
)
|| (elOptionType === 'path'
- && hasOwnPathData(elOptionShape)
- && getPathData(elOptionShape) !== elInner.customPathData
+ && hasOwnPathData(elOptionShape as CustomSVGPathOption['shape'])
+ && getPathData(elOptionShape as CustomSVGPathOption['shape']) !== elInner.customPathData
)
|| (elOptionType === 'image'
&& hasOwn(elOptionStyle, 'image')
@@ -1323,7 +1324,8 @@ function mergeChildren(
// Do not supprot leave elements that are not mentioned in the latest
// `renderItem` return. Otherwise users may not have a clear and simple
// concept that how to contorl all of the elements.
- applyLeaveTransition(el.childAt(i), seriesModel);
+ const child = el.childAt(i);
+ applyLeaveTransition(child, customInnerStore(el).option, seriesModel);
}
}
@@ -1377,7 +1379,7 @@ function processAddUpdate(
function processRemove(this: DataDiffer<DiffGroupContext>, oldIndex: number): void {
const context = this.context;
const child = context.oldChildren[oldIndex];
- applyLeaveTransition(child, context.seriesModel);
+ applyLeaveTransition(child, customInnerStore(child).option, context.seriesModel);
}
/**
diff --git a/src/chart/helper/Symbol.ts b/src/chart/helper/Symbol.ts
index be2a5a4..8f994eb 100644
--- a/src/chart/helper/Symbol.ts
+++ b/src/chart/helper/Symbol.ts
@@ -198,8 +198,8 @@ class Symbol extends graphic.Group {
}
if (disableAnimation) {
- // Must stop remove animation manually if don't call initProps or updateProps.
- this.childAt(0).stopAnimation('remove');
+ // Must stop leave transition manually if don't call initProps or updateProps.
+ this.childAt(0).stopAnimation('leave');
}
this._seriesModel = seriesModel;
diff --git a/src/component/graphic/GraphicModel.ts b/src/component/graphic/GraphicModel.ts
index 270b7b1..811c715 100644
--- a/src/component/graphic/GraphicModel.ts
+++ b/src/component/graphic/GraphicModel.ts
@@ -26,7 +26,8 @@ import {
ZRStyleProps,
OptionId,
CommonTooltipOption,
- AnimationOptionMixin
+ AnimationOptionMixin,
+ AnimationOption
} from '../../util/types';
import ComponentModel from '../../model/Component';
import Element, { ElementTextConfig } from 'zrender/src/Element';
@@ -115,6 +116,9 @@ interface GraphicComponentBaseElementOption extends
tooltip?: CommonTooltipOption<unknown>;
+ enterAnimation?: AnimationOption
+ updateAnimation?: AnimationOption
+ leaveAnimation?: AnimationOption
};
@@ -223,7 +227,6 @@ export function setKeyInfoToNewElOption(
newElOption.parentOption = null;
}
-const TRANSITION_PROPS = ['transition', 'enterFrom', 'leaveTo'] as const;
function isSetLoc(
obj: GraphicComponentElementOption,
props: ('left' | 'right' | 'top' | 'bottom')[]
@@ -282,8 +285,13 @@ function mergeNewElOptionToExist(
}
}
+const TRANSITION_PROPS_TO_COPY = ['transition', 'enterFrom', 'leaveTo'];
+const ROOT_TRANSITION_PROPS_TO_COPY =
+ TRANSITION_PROPS_TO_COPY.concat(['enterAnimation', 'updateAnimation', 'leaveAnimation']);
function copyTransitionInfo(
- target: GraphicComponentElementOption, source: GraphicComponentElementOption, targetProp?: string
+ target: GraphicComponentElementOption,
+ source: GraphicComponentElementOption,
+ targetProp?: string
) {
if (targetProp) {
if (!(target as any)[targetProp]
@@ -299,10 +307,11 @@ function copyTransitionInfo(
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];
+ const props = targetProp ? TRANSITION_PROPS_TO_COPY : ROOT_TRANSITION_PROPS_TO_COPY;
+ for (let i = 0; i < props.length; i++) {
+ const prop = props[i];
+ if ((target as any)[prop] == null && (source as any)[prop] != null) {
+ (target as any)[prop] = (source as any)[prop];
}
}
}
diff --git a/src/component/graphic/GraphicView.ts b/src/component/graphic/GraphicView.ts
index eff35f3..b5d1131 100644
--- a/src/component/graphic/GraphicView.ts
+++ b/src/component/graphic/GraphicView.ts
@@ -60,12 +60,11 @@ const nonShapeGraphicElements = {
type NonShapeGraphicElementType = keyof typeof nonShapeGraphicElements;
export const inner = modelUtil.makeInner<{
- widthOption: number;
- heightOption: number;
width: number;
height: number;
isNew: boolean;
id: string;
+ option: GraphicComponentElementOption
}, Element>();
// ------------------------
// View
@@ -184,7 +183,7 @@ export class GraphicComponentView extends ComponentView {
}
}
else if ($action === 'replace') {
- removeEl(elExisting, elMap, graphicModel);
+ removeEl(elExisting, elOption, elMap, graphicModel);
const el = createEl(id, targetElParent, elOption.type, elMap);
if (el) {
applyUpdateTransition(
@@ -198,7 +197,7 @@ export class GraphicComponentView extends ComponentView {
}
else if ($action === 'remove') {
updateLeaveTo(elExisting, elOption);
- removeEl(elExisting, elMap, graphicModel);
+ removeEl(elExisting, elOption, elMap, graphicModel);
}
const el = elMap.get(id);
@@ -217,8 +216,7 @@ export class GraphicComponentView extends ComponentView {
if (el) {
const elInner = inner(el);
- elInner.widthOption = (elOption as GraphicComponentGroupOption).width;
- elInner.heightOption = (elOption as GraphicComponentGroupOption).height;
+ elInner.option = elOption;
setEventData(el, graphicModel, elOption);
graphicUtil.setTooltipConfig({
@@ -262,11 +260,11 @@ export class GraphicComponentView extends ComponentView {
const elInner = inner(el);
const parentElInner = inner(parentEl);
elInner.width = parsePercent(
- elInner.widthOption,
+ (elInner.option as GraphicComponentGroupOption).width,
isParentRoot ? apiWidth : parentElInner.width
) || 0;
elInner.height = parsePercent(
- elInner.heightOption,
+ (elInner.option as GraphicComponentGroupOption).height,
isParentRoot ? apiHeight : parentElInner.height
) || 0;
}
@@ -333,7 +331,7 @@ export class GraphicComponentView extends ComponentView {
private _clear(): void {
const elMap = this._elMap;
elMap.each((el) => {
- removeEl(el, elMap, this._lastGraphicModel);
+ removeEl(el, inner(el).option, elMap, this._lastGraphicModel);
});
this._elMap = zrUtil.createHashMap();
}
@@ -373,13 +371,18 @@ function createEl(
return el;
}
-function removeEl(elExisting: Element, elMap: ElementMap, graphicModel: GraphicComponentModel): void {
+function removeEl(
+ elExisting: Element,
+ elOption: GraphicComponentElementOption,
+ elMap: ElementMap,
+ graphicModel: GraphicComponentModel
+): void {
const existElParent = elExisting && elExisting.parent;
if (existElParent) {
elExisting.type === 'group' && elExisting.traverse(function (el) {
- removeEl(el, elMap, graphicModel);
+ removeEl(el, elOption, elMap, graphicModel);
});
- applyLeaveTransition(elExisting, graphicModel);
+ applyLeaveTransition(elExisting, elOption, graphicModel);
elMap.removeKey(inner(elExisting).id);
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org