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 c46b928 feat(ani): support keyframe animation in custom series
c46b928 is described below
commit c46b92851782c562d7ffe510d0ee586f709a8f95
Author: pissang <[email protected]>
AuthorDate: Fri Dec 3 14:59:23 2021 +0800
feat(ani): support keyframe animation in custom series
support multiple keyframe animation
---
src/animation/customGraphicKeyframeAnimation.ts | 19 ++++--
src/chart/custom/CustomSeries.ts | 10 +++
src/chart/custom/CustomView.ts | 9 +++
src/component/graphic/GraphicModel.ts | 8 +--
src/component/graphic/GraphicView.ts | 3 +-
test/graphic-animation.html | 87 +++++++++++++++++++++++--
test/graphic-cases.html | 49 ++++++++++----
7 files changed, 156 insertions(+), 29 deletions(-)
diff --git a/src/animation/customGraphicKeyframeAnimation.ts
b/src/animation/customGraphicKeyframeAnimation.ts
index 20c9d7e..609efd3 100644
--- a/src/animation/customGraphicKeyframeAnimation.ts
+++ b/src/animation/customGraphicKeyframeAnimation.ts
@@ -19,7 +19,7 @@
import { AnimationEasing } from 'zrender/src/animation/easing';
import Element from 'zrender/src/Element';
-import { keys, filter, each } from 'zrender/src/core/util';
+import { keys, filter, each, isArray } from 'zrender/src/core/util';
import { ELEMENT_ANIMATABLE_PROPS } from './customGraphicTransition';
import { AnimationOption, AnimationOptionMixin, Dictionary } from
'../util/types';
import { Model } from '../echarts.all';
@@ -56,10 +56,17 @@ export function stopPreviousKeyframeAnimationAndRestore(el:
Element) {
export function applyKeyframeAnimation<T extends Record<string, any>>(
el: Element,
- animationOpts: ElementKeyframeAnimationOption<T>,
+ animationOpts: ElementKeyframeAnimationOption<T> |
ElementKeyframeAnimationOption<T>[],
animatableModel: Model<AnimationOptionMixin>
) {
- if (!animatableModel.isAnimationEnabled()) {
+ if (!animatableModel.isAnimationEnabled() || !animationOpts) {
+ return;
+ }
+
+ if (isArray(animationOpts)) {
+ each(animationOpts, singleAnimationOpts => {
+ applyKeyframeAnimation(el, singleAnimationOpts, animatableModel);
+ });
return;
}
@@ -77,7 +84,7 @@ export function applyKeyframeAnimation<T extends
Record<string, any>>(
const stateToRestore: StateToRestore = getStateToRestore(el);
- function applyKeyframeAnimationOnProp(targetPropName: typeof
ELEMENT_ANIMATABLE_PROPS[number]) {
+ each(ELEMENT_ANIMATABLE_PROPS, (targetPropName) => {
if (targetPropName && !(el as any)[targetPropName]) {
return;
}
@@ -144,7 +151,5 @@ export function applyKeyframeAnimation<T extends
Record<string, any>>(
animator
.delay(animationOpts.delay || 0)
.start(animationOpts.easing);
- }
-
- each(ELEMENT_ANIMATABLE_PROPS, applyKeyframeAnimationOnProp);
+ });
}
\ No newline at end of file
diff --git a/src/chart/custom/CustomSeries.ts b/src/chart/custom/CustomSeries.ts
index 5a8e998..6477df2 100644
--- a/src/chart/custom/CustomSeries.ts
+++ b/src/chart/custom/CustomSeries.ts
@@ -72,6 +72,7 @@ import {
TransitionDuringAPI
} from '../../animation/customGraphicTransition';
import { TransformProp } from 'zrender/src/core/Transformable';
+import { ElementKeyframeAnimationOption } from
'../../animation/customGraphicKeyframeAnimation';
export type CustomExtraElementInfo = Dictionary<unknown>;
@@ -154,6 +155,8 @@ export interface CustomGroupOption extends
CustomBaseElementOption, TransitionOp
diffChildrenByName?: boolean;
children: CustomElementOption[];
$mergeChildren?: false | 'byName' | 'byIndex';
+
+ keyframeAnimation?: ElementKeyframeAnimationOption<GroupProps> |
ElementKeyframeAnimationOption<GroupProps>[]
}
export interface CustomBaseZRPathOption<T extends PathProps['shape'] =
PathProps['shape']>
extends CustomDisplayableOption, ShapeMorphingOption,
TransitionOptionMixin<PathProps & {shape: T}> {
@@ -161,6 +164,9 @@ export interface CustomBaseZRPathOption<T extends
PathProps['shape'] = PathProps
shape?: T & TransitionOptionMixin<T>;
style?: PathProps['style'] & TransitionOptionMixin<PathStyleProps>
during?(params: TransitionDuringAPI<PathStyleProps, T>): void;
+
+ keyframeAnimation?: ElementKeyframeAnimationOption<PathProps & { shape: T
}>
+ | ElementKeyframeAnimationOption<PathProps & { shape: T }>[]
}
interface BuiltinShapes {
@@ -211,6 +217,8 @@ export interface CustomImageOption extends
CustomDisplayableOption, TransitionOp
emphasis?: CustomImageOptionOnState;
blur?: CustomImageOptionOnState;
select?: CustomImageOptionOnState;
+
+ keyframeAnimation?: ElementKeyframeAnimationOption<ImageProps> |
ElementKeyframeAnimationOption<ImageProps>[]
}
export interface CustomTextOptionOnState extends
CustomDisplayableOptionOnState {
@@ -222,6 +230,8 @@ export interface CustomTextOption extends
CustomDisplayableOption, TransitionOpt
emphasis?: CustomTextOptionOnState;
blur?: CustomTextOptionOnState;
select?: CustomTextOptionOnState;
+
+ keyframeAnimation?: ElementKeyframeAnimationOption<TextProps> |
ElementKeyframeAnimationOption<TextProps>[]
}
export type CustomElementOption = CustomPathOption
diff --git a/src/chart/custom/CustomView.ts b/src/chart/custom/CustomView.ts
index abfc4fa..32c0b99 100644
--- a/src/chart/custom/CustomView.ts
+++ b/src/chart/custom/CustomView.ts
@@ -95,6 +95,10 @@ import {
applyUpdateTransition,
ElementRootTransitionProp
} from '../../animation/customGraphicTransition';
+import {
+ applyKeyframeAnimation,
+ stopPreviousKeyframeAnimationAndRestore
+} from '../../animation/customGraphicKeyframeAnimation';
const EMPHASIS = 'emphasis' as const;
const NORMAL = 'normal' as const;
@@ -442,6 +446,9 @@ function updateElNormal(
isTextContent: boolean
): void {
+ // Stop and restore before update any other attributes.
+ stopPreviousKeyframeAnimationAndRestore(el);
+
const txCfgOpt = attachedTxInfo && attachedTxInfo.normal.cfg;
if (txCfgOpt) {
// PENDING: whether use user object directly rather than clone?
@@ -494,6 +501,8 @@ function updateElNormal(
clearStyle: true
});
+ applyKeyframeAnimation(el, elOption.keyframeAnimation, seriesModel);
+
if (!isTextContent) {
// `elOption.info` enables user to mount some info on
// elements and use them in event handlers.
diff --git a/src/component/graphic/GraphicModel.ts
b/src/component/graphic/GraphicModel.ts
index eab5b9a..bf619a1 100644
--- a/src/component/graphic/GraphicModel.ts
+++ b/src/component/graphic/GraphicModel.ts
@@ -153,21 +153,21 @@ export interface GraphicComponentGroupOption
// children: Omit<GraphicComponentElementOption, 'focus' | 'blurScope'>[];
children: GraphicComponentElementOption[];
- keyframeAnimation?: ElementKeyframeAnimationOption<GroupProps>
+ keyframeAnimation?: ElementKeyframeAnimationOption<GroupProps> |
ElementKeyframeAnimationOption<GroupProps>[]
};
export interface GraphicComponentZRPathOption
extends GraphicComponentDisplayableOption,
TransitionOptionMixin<PathProps> {
shape?: PathProps['shape'] & TransitionOptionMixin<PathProps['shape']>;
style?: PathStyleProps & TransitionOptionMixin<PathStyleProps>
- keyframeAnimation?: ElementKeyframeAnimationOption<PathProps>;
+ keyframeAnimation?: ElementKeyframeAnimationOption<PathProps> |
ElementKeyframeAnimationOption<PathProps>[];
}
export interface GraphicComponentImageOption
extends GraphicComponentDisplayableOption,
TransitionOptionMixin<ImageProps> {
type?: 'image';
style?: ImageStyleProps & TransitionOptionMixin<ImageStyleProps>;
- keyframeAnimation?: ElementKeyframeAnimationOption<ImageProps>;
+ keyframeAnimation?: ElementKeyframeAnimationOption<ImageProps> |
ElementKeyframeAnimationOption<ImageProps>[];
}
// TODO: states?
// interface GraphicComponentImageOptionOnState extends
GraphicComponentDisplayableOptionOnState {
@@ -178,7 +178,7 @@ interface GraphicComponentTextOption
type?: 'text';
style?: TextStyleProps & TransitionOptionMixin<TextStyleProps>;
- keyframeAnimation?: ElementKeyframeAnimationOption<TextProps>;
+ keyframeAnimation?: ElementKeyframeAnimationOption<TextProps> |
ElementKeyframeAnimationOption<TextProps>[];
}
export type GraphicComponentElementOption =
GraphicComponentGroupOption |
diff --git a/src/component/graphic/GraphicView.ts
b/src/component/graphic/GraphicView.ts
index f790db8..131a223 100644
--- a/src/component/graphic/GraphicView.ts
+++ b/src/component/graphic/GraphicView.ts
@@ -222,7 +222,6 @@ export class GraphicComponentView extends ComponentView {
if (el) {
const elInner = inner(el);
- const keyframeAnimation = elOption.keyframeAnimation;
el.setTextConfig(textConfig);
@@ -236,7 +235,7 @@ export class GraphicComponentView extends ComponentView {
itemTooltipOption: elOption.tooltip
});
- keyframeAnimation && applyKeyframeAnimation(el,
keyframeAnimation, graphicModel);
+ applyKeyframeAnimation(el, elOption.keyframeAnimation,
graphicModel);
}
});
}
diff --git a/test/graphic-animation.html b/test/graphic-animation.html
index 4ebf2db..7ddea94 100644
--- a/test/graphic-animation.html
+++ b/test/graphic-animation.html
@@ -93,6 +93,79 @@ under the License.
<script>
require(['echarts'/*, 'map/js/china' */], function (echarts) {
+ let greenCirclePosition = 100;
+ var option = {
+ graphic: {
+ elements: [{
+ type: 'circle',
+ x: greenCirclePosition,
+ y: 50,
+ transition: 'all',
+ shape: {
+ cx: 0,
+ cy: 0,
+ r: 50
+ },
+ style: {
+ fill: 'green'
+ },
+ }, {
+ type: 'circle',
+ x: 100,
+ y: 200,
+ shape: {
+ cx: 0,
+ cy: 0,
+ r: 50
+ },
+ keyframeAnimation: {
+ duration: 1000,
+ loop: true,
+ keyframes: [{
+ percent: 0,
+ x: 100
+ }, {
+ percent: 0.5,
+ easing: 'sinusoidalInOut',
+ x: 500
+ }, {
+ percent: 1,
+ easing: 'sinusoidalInOut',
+ x: 100
+ }]
+ },
+ style: {
+ fill: 'orange'
+ }
+ }]
+ }
+ }
+
+ var chart = testHelper.create(echarts, 'main1', {
+ title: [
+ 'Update on other elements should not affect the
keyframe animation'
+ ],
+ buttons: [{
+ text: 'Move Green Circle',
+ onclick() {
+ greenCirclePosition = 500 - greenCirclePosition;
+ chart.setOption({
+ graphic: {
+ elements: [{
+ x: greenCirclePosition
+ }]
+ }
+ });
+ }
+ }],
+ option: option
+ });
+
+ });
+ </script>
+
+ <script>
+ require(['echarts'/*, 'map/js/china' */], function (echarts) {
var option = {
graphic: {
elements: [{
@@ -110,7 +183,7 @@ under the License.
lineWidth: 1
},
keyframeAnimation: {
- duration: 4000,
+ duration: 3000,
loop: true,
keyframes: [{
percent: 0.7,
@@ -120,6 +193,12 @@ under the License.
lineDash: [200, 0]
}
}, {
+ // Hold a bit.
+ percent: 0.8,
+ style: {
+ fill: 'transparent'
+ }
+ }, {
percent: 1,
style: {
fill: 'black'
@@ -130,15 +209,15 @@ under the License.
}
}
- var chart = testHelper.create(echarts, 'main1', {
+ var chart = testHelper.create(echarts, 'main2', {
title: [
- 'Text Stroke'
+ 'Text Stroke Animation'
],
option: option
});
});
- </script>
+ </script>
</body>
</html>
diff --git a/test/graphic-cases.html b/test/graphic-cases.html
index 625a1c1..8b913ed 100644
--- a/test/graphic-cases.html
+++ b/test/graphic-cases.html
@@ -38,17 +38,18 @@ under the License.
<div id="main0"></div>
- <div id="main-legacy-style"></div>
+ <div id="main1"></div>
+ <div id="main2"></div>
<script>
- require(['echarts'/*, 'map/js/china' */], function (echarts) {
- var option;
+ const imageURI =
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANwAAADcCAYAAAAbWs+BAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4gIUARQAHY8+4wAAApBJREFUeNrt3cFqAjEUhlEjvv8rXzciiiBGk/He5JxdN2U649dY+KmnEwAAAAAv2uMXEeGOwERntwAEB4IDBAeCAwQHggPBAYIDwQGCA8GB4ADBgeAAwYHgAMGB4EBwgOCgpkuKq2it/r8Li2hbvGKqP6s/PycnHHv9YvSWEgQHCA4EBwgOBAeCAwQHggMEByXM+QRUE6D3suwuPafDn5MTDg50KXnVPSdxa54y/oYDwQGCA8EBggPBAYIDwYHggBE+X5rY3Y3Tey97Nn2eU+rnlGfaZa6Ft5SA4EBwgOBAcCA4QHAgOEBwIDjgZu60
[...]
- const imageURI =
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANwAAADcCAYAAAAbWs+BAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4gIUARQAHY8+4wAAApBJREFUeNrt3cFqAjEUhlEjvv8rXzciiiBGk/He5JxdN2U649dY+KmnEwAAAAAv2uMXEeGOwERntwAEB4IDBAeCAwQHggPBAYIDwQGCA8GB4ADBgeAAwYHgAMGB4EBwgOCgpkuKq2it/r8Li2hbvGKqP6s/PycnHHv9YvSWEgQHCA4EBwgOBAeCAwQHggMEByXM+QRUE6D3suwuPafDn5MTDg50KXnVPSdxa54y/oYDwQGCA8EBggPBAYIDwYHggBE+X5rY3Y3Tey97Nn2eU+rnlGfaZa6Ft5SA4EBwgOBAcCA4QHAgOEBwIDjg
[...]
+ require(['echarts'/*, 'map/js/china' */], function (echarts) {
+ var option;
option = {
@@ -132,10 +133,6 @@ under the License.
require(['echarts'/*, 'map/js/china' */], function (echarts) {
var option;
-
- const imageURI =
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANwAAADcCAYAAAAbWs+BAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4gIUARQAHY8+4wAAApBJREFUeNrt3cFqAjEUhlEjvv8rXzciiiBGk/He5JxdN2U649dY+KmnEwAAAAAv2uMXEeGOwERntwAEB4IDBAeCAwQHggPBAYIDwQGCA8GB4ADBgeAAwYHgAMGB4EBwgOCgpkuKq2it/r8Li2hbvGKqP6s/PycnHHv9YvSWEgQHCA4EBwgOBAeCAwQHggMEByXM+QRUE6D3suwuPafDn5MTDg50KXnVPSdxa54y/oYDwQGCA8EBggPBAYIDwYHggBE+X5rY3Y3Tey97Nn2eU+rnlGfaZa6Ft5SA4EBwgOBAcCA4QHAgOEBwIDjg
[...]
-
-
option = {
graphic: [{
type: 'circle',
@@ -176,14 +173,12 @@ under the License.
style: {
width: 50,
height: 100,
- image: imageURI,
- storke: 'blue',
- lineWidth: 5
+ image: imageURI
}
}]
};
- var chart = testHelper.create(echarts, 'main-legacy-style', {
+ var chart = testHelper.create(echarts, 'main1', {
title: [
'Legacy API:',
'Should the same as new API'
@@ -194,6 +189,36 @@ under the License.
});
</script>
+ <script>
+ require(['echarts'/*, 'map/js/china' */], function (echarts) {
+ var option;
+ option = {
+ graphic: {
+ elements: [{
+ type: 'text',
+ x: 100,
+ y: 100,
+ style: {
+ text: 'Apache ECharts',
+ fill: 'none',
+ fontSize: 50,
+ fontWeight: 'bold',
+ stroke: '#000',
+ lineWidth: 1
+ }
+ }]
+ }
+ };
+
+ var chart = testHelper.create(echarts, 'main2', {
+ title: [
+ 'Stroke Only Text'
+ ],
+ option: option
+ });
+
+ });
+ </script>
</body>
</html>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]