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/18 15:28:19 UTC
[echarts] branch graphic-animation updated: feat(graphic): add transition api
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 fb3daa2 feat(graphic): add transition api
fb3daa2 is described below
commit fb3daa222758e1fcb300126a4c302f3f27a014c7
Author: pissang <bm...@gmail.com>
AuthorDate: Thu Nov 18 23:26:30 2021 +0800
feat(graphic): add transition api
---
src/animation/customGraphicTransitionHelper.ts | 28 +++---
src/chart/custom/CustomSeries.ts | 20 ++--
src/chart/custom/CustomView.ts | 10 +-
src/component/graphic/GraphicModel.ts | 24 +++--
src/component/graphic/GraphicView.ts | 58 ++++++++----
test/graphic-transition.html | 124 +++++++++++++++++++++++++
6 files changed, 207 insertions(+), 57 deletions(-)
diff --git a/src/animation/customGraphicTransitionHelper.ts b/src/animation/customGraphicTransitionHelper.ts
index 45a5938..6ae2106 100644
--- a/src/animation/customGraphicTransitionHelper.ts
+++ b/src/animation/customGraphicTransitionHelper.ts
@@ -42,13 +42,13 @@ const LEGACY_TRANSFORM_PROPS = {
type LegacyTransformProp = keyof typeof LEGACY_TRANSFORM_PROPS;
export type CustomTransitionProps = string | string[];
-export interface ElementTransitionOptionMixin {
+export interface TransitionOptionMixin {
transition?: CustomTransitionProps;
enterFrom?: Dictionary<unknown>;
leaveTo?: Dictionary<unknown>;
};
-export type ElementTransformTransitionOptionMixin = {
+export type ElementTransitionOptionMixin = {
transition?: ElementRootTransitionProp | ElementRootTransitionProp[];
enterFrom?: Dictionary<number>;
leaveTo?: Dictionary<number>;
@@ -72,16 +72,16 @@ interface LooseElementProps extends ElementProps {
}
type TransitionElementOption = Partial<Record<TransformProp, number>> & {
- shape?: Dictionary<any> & ElementTransitionOptionMixin
- style?: PathStyleProps & ElementTransitionOptionMixin
- extra?: Dictionary<any> & ElementTransitionOptionMixin
+ shape?: Dictionary<any> & TransitionOptionMixin
+ style?: PathStyleProps & TransitionOptionMixin
+ extra?: Dictionary<any> & TransitionOptionMixin
invisible?: boolean
silent?: boolean
autoBatch?: boolean
ignore?: boolean
during?: (params: TransitionDuringAPI) => void
-} & ElementTransformTransitionOptionMixin;
+} & ElementTransitionOptionMixin;
const transitionInnerStore = makeInner<{
leaveToProps: ElementProps;
@@ -141,7 +141,7 @@ export interface TransitionDuringAPI<
getStyle<T extends keyof StyleOpt>(key: T): StyleOpt[T];
};
-export function updateTransition(
+export function applyUpdateTransition(
el: Element,
elOption: TransitionElementOption,
animatableModel?: Model<AnimationOptionMixin>,
@@ -171,20 +171,22 @@ export function updateTransition(
styleOpt ? el.dirty() : el.markRedraw();
}
-export function leaveTransition(
+export function applyLeaveTransition(
el: Element,
- seriesModel: Model<AnimationOptionMixin>
+ animatableModel: Model<AnimationOptionMixin>,
+ onRemove?: () => void
): void {
if (el) {
const parent = el.parent;
const leaveToProps = transitionInnerStore(el).leaveToProps;
leaveToProps
- ? updateProps(el, leaveToProps, seriesModel, {
+ ? updateProps(el, leaveToProps, animatableModel, {
cb: function () {
parent.remove(el);
+ onRemove && onRemove();
}
})
- : parent.remove(el);
+ : (parent.remove(el), onRemove && onRemove());
}
}
@@ -433,7 +435,7 @@ function prepareShapeOrExtraTransitionFrom(
isInit: boolean
): void {
- const attrOpt: Dictionary<unknown> & ElementTransitionOptionMixin = (elOption as any)[mainAttr];
+ const attrOpt: Dictionary<unknown> & TransitionOptionMixin = (elOption as any)[mainAttr];
if (!attrOpt) {
return;
}
@@ -498,7 +500,7 @@ function prepareShapeOrExtraAllPropsFinal(
elOption: TransitionElementOption,
allProps: LooseElementProps
): void {
- const attrOpt: Dictionary<unknown> & ElementTransitionOptionMixin = (elOption as any)[mainAttr];
+ const attrOpt: Dictionary<unknown> & TransitionOptionMixin = (elOption as any)[mainAttr];
if (!attrOpt) {
return;
}
diff --git a/src/chart/custom/CustomSeries.ts b/src/chart/custom/CustomSeries.ts
index cf72658..3ef34d0 100644
--- a/src/chart/custom/CustomSeries.ts
+++ b/src/chart/custom/CustomSeries.ts
@@ -65,8 +65,8 @@ import {
} from '../../util/graphic';
import { TextStyleProps } from 'zrender/src/graphic/Text';
import {
- ElementTransformTransitionOptionMixin,
ElementTransitionOptionMixin,
+ TransitionOptionMixin,
TransitionBaseDuringAPI,
TransitionDuringAPI
} from '../../animation/customGraphicTransitionHelper';
@@ -104,7 +104,7 @@ type ShapeMorphingOption = {
export interface CustomBaseElementOption extends Partial<Pick<
Element, TransformProp | 'silent' | 'ignore' | 'textConfig'
->>, ElementTransformTransitionOptionMixin {
+>>, ElementTransitionOptionMixin {
// element type, required.
type: string;
id?: string;
@@ -116,7 +116,7 @@ export interface CustomBaseElementOption extends Partial<Pick<
// `false` means remove the clipPath
clipPath?: CustomBaseZRPathOption | false;
// `extra` can be set in any el option for custom prop for annimation duration.
- extra?: Dictionary<unknown> & ElementTransitionOptionMixin;
+ extra?: Dictionary<unknown> & TransitionOptionMixin;
// updateDuringAnimation
during?(params: TransitionBaseDuringAPI): void;
};
@@ -124,7 +124,7 @@ export interface CustomBaseElementOption extends Partial<Pick<
export interface CustomDisplayableOption extends CustomBaseElementOption, Partial<Pick<
Displayable, 'zlevel' | 'z' | 'z2' | 'invisible'
>> {
- style?: ZRStyleProps & ElementTransitionOptionMixin;
+ style?: ZRStyleProps & TransitionOptionMixin;
during?(params: TransitionDuringAPI): void;
/**
* @deprecated
@@ -139,7 +139,7 @@ export interface CustomDisplayableOptionOnState extends Partial<Pick<
Displayable, TransformProp | 'textConfig' | 'z2'
>> {
// `false` means remove emphasis trigger.
- style?: (ZRStyleProps & ElementTransitionOptionMixin) | false;
+ style?: (ZRStyleProps & TransitionOptionMixin) | false;
during?(params: TransitionDuringAPI): void;
@@ -156,7 +156,7 @@ export interface CustomGroupOption extends CustomBaseElementOption {
export interface CustomBaseZRPathOption<T extends PathProps['shape'] = PathProps['shape']>
extends CustomDisplayableOption, ShapeMorphingOption {
autoBatch?: boolean;
- shape?: T & ElementTransitionOptionMixin;
+ shape?: T & TransitionOptionMixin;
style?: PathProps['style']
during?(params: TransitionDuringAPI<PathStyleProps, T>): void;
}
@@ -201,22 +201,22 @@ export type CustomPathOption = CreateCustomBuitinPathOption<keyof BuiltinShapes>
| CustomSVGPathOption;
export interface CustomImageOptionOnState extends CustomDisplayableOptionOnState {
- style?: ImageStyleProps & ElementTransitionOptionMixin;
+ style?: ImageStyleProps & TransitionOptionMixin;
}
export interface CustomImageOption extends CustomDisplayableOption {
type: 'image';
- style?: ImageStyleProps & ElementTransitionOptionMixin;
+ style?: ImageStyleProps & TransitionOptionMixin;
emphasis?: CustomImageOptionOnState;
blur?: CustomImageOptionOnState;
select?: CustomImageOptionOnState;
}
export interface CustomTextOptionOnState extends CustomDisplayableOptionOnState {
- style?: TextStyleProps & ElementTransitionOptionMixin;
+ style?: TextStyleProps & TransitionOptionMixin;
}
export interface CustomTextOption extends CustomDisplayableOption {
type: 'text';
- style?: TextStyleProps & ElementTransitionOptionMixin;
+ style?: TextStyleProps & TransitionOptionMixin;
emphasis?: CustomTextOptionOnState;
blur?: CustomTextOptionOnState;
select?: CustomTextOptionOnState;
diff --git a/src/chart/custom/CustomView.ts b/src/chart/custom/CustomView.ts
index 2621341..3dcf328 100644
--- a/src/chart/custom/CustomView.ts
+++ b/src/chart/custom/CustomView.ts
@@ -90,7 +90,7 @@ import CustomSeriesModel, {
} from './CustomSeries';
import { PatternObject } from 'zrender/src/graphic/Pattern';
import { CustomSeriesOption } from '../../export/option';
-import { leaveTransition, updateTransition } from '../../animation/customGraphicTransitionHelper';
+import { applyLeaveTransition, applyUpdateTransition } from '../../animation/customGraphicTransitionHelper';
const EMPHASIS = 'emphasis' as const;
const NORMAL = 'normal' as const;
@@ -218,7 +218,7 @@ export default class CustomChartView extends ChartView {
);
})
.remove(function (oldIdx) {
- leaveTransition(oldData.getItemGraphicEl(oldIdx), customSeries);
+ applyLeaveTransition(oldData.getItemGraphicEl(oldIdx), customSeries);
})
.update(function (newIdx, oldIdx) {
const oldEl = oldData.getItemGraphicEl(oldIdx);
@@ -475,7 +475,7 @@ function updateElNormal(
}
}
- updateTransition(el, elOption, seriesModel, dataIndex, isInit);
+ applyUpdateTransition(el, elOption, seriesModel, dataIndex, isInit);
if (!isTextContent) {
@@ -1310,7 +1310,7 @@ 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.
- leaveTransition(el.childAt(i), seriesModel);
+ applyLeaveTransition(el.childAt(i), seriesModel);
}
}
@@ -1364,7 +1364,7 @@ function processAddUpdate(
function processRemove(this: DataDiffer<DiffGroupContext>, oldIndex: number): void {
const context = this.context;
const child = context.oldChildren[oldIndex];
- leaveTransition(child, context.seriesModel);
+ applyLeaveTransition(child, context.seriesModel);
}
/**
diff --git a/src/component/graphic/GraphicModel.ts b/src/component/graphic/GraphicModel.ts
index 5131597..92f0160 100644
--- a/src/component/graphic/GraphicModel.ts
+++ b/src/component/graphic/GraphicModel.ts
@@ -25,7 +25,8 @@ import {
Dictionary,
ZRStyleProps,
OptionId,
- CommonTooltipOption
+ CommonTooltipOption,
+ AnimationOptionMixin
} from '../../util/types';
import ComponentModel from '../../model/Component';
import Element, { ElementTextConfig } from 'zrender/src/Element';
@@ -35,6 +36,7 @@ import { ImageStyleProps } from 'zrender/src/graphic/Image';
import GlobalModel from '../../model/Global';
import { TextStyleProps } from 'zrender/src/graphic/Text';
import { copyLayoutParams, mergeLayoutParam } from '../../util/layout';
+import { ElementTransitionOptionMixin, TransitionOptionMixin } from '../../animation/customGraphicTransitionHelper';
interface GraphicComponentBaseElementOption extends
Partial<Pick<
@@ -116,9 +118,11 @@ interface GraphicComponentBaseElementOption extends
export type TransformProp = 'x' | 'y' | 'scaleX' | 'scaleY' | 'originX' | 'originY' | 'skewX' | 'skewY' | 'rotation';
export interface GraphicComponentDisplayableOption extends
- GraphicComponentBaseElementOption, Partial<Pick<Displayable, 'zlevel' | 'z' | 'z2' | 'invisible' | 'cursor'>> {
+ GraphicComponentBaseElementOption,
+ ElementTransitionOptionMixin,
+ Partial<Pick<Displayable, 'zlevel' | 'z' | 'z2' | 'invisible' | 'cursor'>> {
- style?: ZRStyleProps;
+ style?: ZRStyleProps & TransitionOptionMixin
}
// TODO: states?
// interface GraphicComponentDisplayableOptionOnState extends Partial<Pick<
@@ -126,7 +130,8 @@ export interface GraphicComponentDisplayableOption extends
// >> {
// style?: ZRStyleProps;
// }
-export interface GraphicComponentGroupOption extends GraphicComponentBaseElementOption {
+export interface GraphicComponentGroupOption
+ extends GraphicComponentBaseElementOption, ElementTransitionOptionMixin {
type?: 'group';
/**
@@ -141,13 +146,13 @@ export interface GraphicComponentGroupOption extends GraphicComponentBaseElement
// TODO: Can only set focus, blur on the root element.
// children: Omit<GraphicComponentElementOption, 'focus' | 'blurScope'>[];
children: GraphicComponentElementOption[];
-}
+};
export interface GraphicComponentZRPathOption extends GraphicComponentDisplayableOption {
- shape?: PathProps['shape'];
+ shape?: PathProps['shape'] & TransitionOptionMixin;
}
export interface GraphicComponentImageOption extends GraphicComponentDisplayableOption {
type?: 'image';
- style?: ImageStyleProps;
+ style?: ImageStyleProps & TransitionOptionMixin;
}
// TODO: states?
// interface GraphicComponentImageOptionOnState extends GraphicComponentDisplayableOptionOnState {
@@ -174,11 +179,10 @@ export type GraphicComponentLooseOption = (GraphicComponentOption | GraphicCompo
mainType?: 'graphic';
};
-export interface GraphicComponentOption extends ComponentOption {
+export interface GraphicComponentOption extends ComponentOption, AnimationOptionMixin {
// Note: elements is always behind its ancestors in this elements array.
elements?: GraphicComponentElementOption[];
-}
-;
+};
export function setKeyInfoToNewElOption(
resultItem: ReturnType<typeof modelUtil.mappingToExists>[number],
diff --git a/src/component/graphic/GraphicView.ts b/src/component/graphic/GraphicView.ts
index 8d3f5c2..e6f5f15 100644
--- a/src/component/graphic/GraphicView.ts
+++ b/src/component/graphic/GraphicView.ts
@@ -37,8 +37,9 @@ import {
GraphicComponentGroupOption,
GraphicComponentElementOption
} from './GraphicModel';
+import { applyLeaveTransition, applyUpdateTransition } from '../../animation/customGraphicTransitionHelper';
-const _nonShapeGraphicElements = {
+const nonShapeGraphicElements = {
// Reserved but not supported in graphic component.
path: null as unknown,
compoundPath: null as unknown,
@@ -48,7 +49,7 @@ const _nonShapeGraphicElements = {
image: graphicUtil.Image,
text: graphicUtil.Text
} as const;
-type NonShapeGraphicElementType = keyof typeof _nonShapeGraphicElements;
+type NonShapeGraphicElementType = keyof typeof nonShapeGraphicElements;
export const inner = modelUtil.makeInner<{
widthOption: number;
@@ -152,16 +153,34 @@ export class GraphicComponentView extends ComponentView {
const $action = elOption.$action || 'merge';
if ($action === 'merge') {
- elExisting
- ? elExisting.attr(elOptionCleaned)
- : createEl(id, targetElParent, elOptionCleaned, elMap);
+ const isInit = !elExisting;
+ let el = elExisting;
+ if (isInit) {
+ el = createEl(id, targetElParent, elOption.type, elMap);
+ }
+ el && applyUpdateTransition(
+ el,
+ elOptionCleaned,
+ graphicModel,
+ 0, // TODO Fixed dataIndex to be 0
+ isInit
+ );
}
else if ($action === 'replace') {
- removeEl(elExisting, elMap);
- createEl(id, targetElParent, elOptionCleaned, elMap);
+ removeEl(elExisting, elMap, graphicModel);
+ const el = createEl(id, targetElParent, elOption.type, elMap);
+ if (el) {
+ applyUpdateTransition(
+ el,
+ elOptionCleaned,
+ graphicModel,
+ 0, // TODO Fixed dataIndex to be 0
+ true
+ );
+ }
}
else if ($action === 'remove') {
- removeEl(elExisting, elMap);
+ removeEl(elExisting, elMap, graphicModel);
}
const el = elMap.get(id);
@@ -266,8 +285,8 @@ export class GraphicComponentView extends ComponentView {
*/
private _clear(): void {
const elMap = this._elMap;
- elMap.each(function (el) {
- removeEl(el, elMap);
+ elMap.each((el) => {
+ removeEl(el, elMap, this._lastGraphicModel);
});
this._elMap = zrUtil.createHashMap();
}
@@ -279,20 +298,19 @@ export class GraphicComponentView extends ComponentView {
function createEl(
id: string,
targetElParent: graphicUtil.Group,
- elOption: GraphicComponentElementOption,
+ graphicType: string,
elMap: ElementMap
-): void {
- const graphicType = elOption.type;
+): Element {
if (__DEV__) {
zrUtil.assert(graphicType, 'graphic type MUST be set');
}
const Clz = (
- zrUtil.hasOwn(_nonShapeGraphicElements, graphicType)
+ zrUtil.hasOwn(nonShapeGraphicElements, graphicType)
// Those graphic elements are not shapes. They should not be
// overwritten by users, so do them first.
- ? _nonShapeGraphicElements[graphicType as NonShapeGraphicElementType]
+ ? nonShapeGraphicElements[graphicType as NonShapeGraphicElementType]
: graphicUtil.getShapeClass(graphicType)
) as { new(opt: GraphicComponentElementOption): Element; };
@@ -300,19 +318,21 @@ function createEl(
zrUtil.assert(Clz, 'graphic type can not be found');
}
- const el = new Clz(elOption);
+ const el = new Clz({});
targetElParent.add(el);
elMap.set(id, el);
inner(el).id = id;
+
+ return el;
}
-function removeEl(elExisting: Element, elMap: ElementMap): void {
+function removeEl(elExisting: Element, elMap: ElementMap, graphicModel: GraphicComponentModel): void {
const existElParent = elExisting && elExisting.parent;
if (existElParent) {
elExisting.type === 'group' && elExisting.traverse(function (el) {
- removeEl(el, elMap);
+ removeEl(el, elMap, graphicModel);
});
+ applyLeaveTransition(elExisting, graphicModel);
elMap.removeKey(inner(elExisting).id);
- existElParent.remove(elExisting);
}
}
// Remove unnecessary props to avoid potential problems.
diff --git a/test/graphic-transition.html b/test/graphic-transition.html
new file mode 100644
index 0000000..20bd92c
--- /dev/null
+++ b/test/graphic-transition.html
@@ -0,0 +1,124 @@
+<!DOCTYPE html>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+
+<html>
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <script src="lib/simpleRequire.js"></script>
+ <script src="lib/config.js"></script>
+ <script src="lib/jquery.min.js"></script>
+ <script src="lib/facePrint.js"></script>
+ <script src="lib/testHelper.js"></script>
+ <!-- <script src="ut/lib/canteen.js"></script> -->
+ <link rel="stylesheet" href="lib/reset.css" />
+ </head>
+ <body>
+ <style>
+ </style>
+
+
+
+ <div id="main0"></div>
+ <div id="main1"></div>
+
+
+
+
+ <script>
+ require(['echarts'/*, 'map/js/china' */], function (echarts) {
+ var option = {
+ graphic: {
+ elements: [{
+ type: 'circle',
+ x: 100,
+ y: 50,
+ shape: {
+ cx: 0,
+ cy: 0,
+ r: 50
+ },
+ style: {
+ fill: 'orange'
+ }
+ }]
+ }
+ }
+
+ var chart = testHelper.create(echarts, 'main0', {
+ title: [
+ 'Basic transition'
+ ],
+ option: option,
+ buttons: [
+ {
+ text: 'Move to x: 200',
+ onclick() {
+ chart.setOption({
+ graphic: {
+ elements: [{
+ type: 'circle',
+ transition: ['x', 'y'],
+ x: 200
+ }]
+ }
+ })
+ }
+ },
+
+
+ {
+ text: 'Move to y: 200',
+ onclick() {
+ chart.setOption({
+ graphic: {
+ elements: [{
+ type: 'circle',
+ transition: ['x', 'y'],
+ y: 200
+ }]
+ }
+ })
+ }
+ },
+ ]
+ });
+
+ });
+ </script>
+
+
+
+
+
+
+
+ <script>
+ require(['echarts'/*, 'map/js/china' */], function (echarts) {
+ var option;
+
+ });
+ </script>
+
+
+ </body>
+</html>
+
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org