Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

introduce new keyframe based animation to graphic component and custom series #16225

Merged
merged 37 commits into from
Dec 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
3d7bf24
refact(graphic): seperate view and model
pissang Nov 18, 2021
6199529
refact(custom): extract common helpers for transition
pissang Nov 18, 2021
fb3daa2
feat(graphic): add transition api
pissang Nov 18, 2021
4501552
refact(transition): rename modules
pissang Nov 19, 2021
cca2206
fix(transition): not do strict checking. some code cleanup
pissang Nov 19, 2021
a3eedb3
feat(transition): support transition to be configured all.
pissang Nov 19, 2021
37c4b1b
feat(transition): fix transition in layout params
pissang Nov 19, 2021
24a9984
feat(transition): fix style transition all
pissang Nov 19, 2021
2eea25e
fix wrong import from lib
pissang Nov 19, 2021
b30b63f
feat(transition) optimize enterFrom and leaveTo. fix style loose in c…
pissang Nov 21, 2021
e449a87
feat(animation): add keyframe based animation
pissang Nov 23, 2021
1c9e3dc
feat(animation):fix null property access
pissang Nov 23, 2021
c021861
Merge branch 'next' into graphic-animation
pissang Nov 24, 2021
459377a
feat(animation): rename to keyframeAnimation
pissang Nov 24, 2021
4566b94
feat(transition): optimize types
pissang Nov 24, 2021
393eb30
feat(transition): add animation config per element
pissang Nov 24, 2021
dc5bc85
feat(animation): fix during in enter and leave animation
pissang Nov 25, 2021
7e96d4c
feat(animation): improve validation logs
pissang Nov 25, 2021
b5b2185
feat(animation): fix some animation abort bug
pissang Nov 25, 2021
6eb6cd9
fix(graphic): fix textConfig not work
pissang Dec 1, 2021
48ba921
feat(animation): restore props after keyframe animation is stopped
pissang Dec 1, 2021
c46b928
feat(ani): support keyframe animation in custom series
pissang Dec 3, 2021
58f96bd
feat(animation): force set duration regardless the kfs
pissang Dec 5, 2021
72ea7a6
feat(graphic): support clipPath
pissang Dec 10, 2021
e8f0c74
feat(emphasis): support gradient color highlight
pissang Dec 13, 2021
c59ebb7
refact(animation): use new duration api
pissang Dec 14, 2021
343ec49
fix(graphic): fix events
pissang Dec 15, 2021
a3d537f
fix(graphic): fix group event not work
pissang Dec 16, 2021
35d1ac8
fix(gauge): display on the start point for NaN value
pissang Dec 16, 2021
79d98b1
fix(animation): fix new added transform props not work
pissang Dec 17, 2021
14b0b8e
style: optimize code according to the review
pissang Dec 17, 2021
19e8bed
test(graphic): add vrt recordings.
pissang Dec 20, 2021
2e5ab99
chore: update zrender to latest nightly version
pissang Dec 20, 2021
8c1a147
Merge branch 'next' into graphic-animation
pissang Dec 20, 2021
e92f3d2
fix(graphic): fix id and name not assigned
pissang Dec 21, 2021
ba196e0
fix(animation): disable animation in the geo roam action.
pissang Dec 21, 2021
86bb85b
chore: update zrender version
pissang Dec 22, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
},
"dependencies": {
"tslib": "2.3.0",
"zrender": "npm:zrender-nightly@^5.3.0-dev.20211125"
"zrender": "npm:zrender-nightly@^5.3.0-dev.20211222"
},
"devDependencies": {
"@babel/code-frame": "7.10.4",
Expand Down
14 changes: 7 additions & 7 deletions src/animation/basicTrasition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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> & {
Expand All @@ -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(
Expand Down Expand Up @@ -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);
}

/**
Expand All @@ -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;
}
}
Expand All @@ -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(
Expand Down
162 changes: 162 additions & 0 deletions src/animation/customGraphicKeyframeAnimation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/*
* 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.
*/

import { AnimationEasing } from 'zrender/src/animation/easing';
import Element from 'zrender/src/Element';
import { keys, filter, each, isArray, indexOf } from 'zrender/src/core/util';
import { ELEMENT_ANIMATABLE_PROPS } from './customGraphicTransition';
import { AnimationOption, AnimationOptionMixin, Dictionary } from '../util/types';
import { Model } from '../echarts.all';
import { getAnimationConfig } from './basicTrasition';
import { warn } from '../util/log';
import { makeInner } from '../util/model';

// Helpers for creating keyframe based animations in custom series and graphic components.

type AnimationKeyframe<T extends Record<string, any>> = T & {
easing?: AnimationEasing
percent?: number // 0 - 1
};

type StateToRestore = Dictionary<any>;
const getStateToRestore = makeInner<StateToRestore, Element>();

const KEYFRAME_EXCLUDE_KEYS = ['percent', 'easing', 'shape', 'style', 'extra'] as const;

export interface ElementKeyframeAnimationOption<Props extends Record<string, any>> extends AnimationOption {
// Animation configuration for keyframe based animation.
loop?: boolean
keyframes?: AnimationKeyframe<Props>[]
}

/**
* Stop previous keyframe animation and restore the attributes.
* Avoid new keyframe animation starts with wrong internal state when the percent: 0 is not set.
*/
export function stopPreviousKeyframeAnimationAndRestore(el: Element) {
// Stop previous keyframe animation.
el.stopAnimation('keyframe');
// Restore
el.attr(getStateToRestore(el));
}

export function applyKeyframeAnimation<T extends Record<string, any>>(
el: Element,
animationOpts: ElementKeyframeAnimationOption<T> | ElementKeyframeAnimationOption<T>[],
animatableModel: Model<AnimationOptionMixin>
) {
if (!animatableModel.isAnimationEnabled() || !animationOpts) {
return;
}

if (isArray(animationOpts)) {
each(animationOpts, singleAnimationOpts => {
applyKeyframeAnimation(el, singleAnimationOpts, animatableModel);
});
return;
}

const keyframes = animationOpts.keyframes;
let duration = animationOpts.duration;

if (animatableModel && duration == null) {
// Default to use duration of config.
// NOTE: animation config from payload will be ignored because they are mainly for transitions.
const config = getAnimationConfig('enter', animatableModel, 0);
duration = config && config.duration;
}

if (!keyframes || !duration) {
return;
}

const stateToRestore: StateToRestore = getStateToRestore(el);

each(ELEMENT_ANIMATABLE_PROPS, (targetPropName) => {
if (targetPropName && !(el as any)[targetPropName]) {
return;
}

let animator: ReturnType<Element['animate']>;
let endFrameIsSet = false;

// Sort keyframes by percent.
keyframes.sort((a, b) => a.percent - b.percent);

each(keyframes, kf => {
// Stop current animation.
const animators = el.animators;
const kfValues = targetPropName ? kf[targetPropName] : kf;

if (__DEV__) {
if (kf.percent >= 1) {
endFrameIsSet = true;
}
}

if (!kfValues) {
return;
}

let propKeys = keys(kfValues);
if (!targetPropName) {
// PENDING performance?
propKeys = filter(propKeys, key => indexOf(KEYFRAME_EXCLUDE_KEYS, key) < 0);
}
if (!propKeys.length) {
return;
}

if (!animator) {
animator = el.animate(targetPropName, animationOpts.loop, true);
animator.scope = 'keyframe';
}
for (let i = 0; i < animators.length; i++) {
// Stop all other animation that is not keyframe.
if (animators[i] !== animator && animators[i].targetName === animator.targetName) {
animators[i].stopTracks(propKeys);
}
}

targetPropName && (stateToRestore[targetPropName] = stateToRestore[targetPropName] || {});

const savedTarget = targetPropName ? stateToRestore[targetPropName] : stateToRestore;
each(propKeys, key => {
// Save original value.
savedTarget[key] = ((targetPropName ? (el as any)[targetPropName] : el) || {})[key];
});

animator.whenWithKeys(duration * kf.percent, kfValues, propKeys, kf.easing);
});
if (!animator) {
return;
}

if (__DEV__) {
if (!endFrameIsSet) {
warn('End frame with percent: 1 is missing in the keyframeAnimation.', true);
}
}

animator
.delay(animationOpts.delay || 0)
.duration(duration)
.start(animationOpts.easing);
});
}
Loading