mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-20 04:14:21 +08:00
fix(animation): properly update Web Animation, clean up types (#19964)
Co-authored-by: manucorporat <manu.mtza@gmail.com>
This commit is contained in:
@ -220,18 +220,20 @@ export interface Animation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type AnimationLifecycle = (currentStep: 0 | 1, animation: Animation) => void;
|
export type AnimationLifecycle = (currentStep: 0 | 1, animation: Animation) => void;
|
||||||
|
export type AnimationKeyFrames = [AnimationKeyFrameEdge, AnimationKeyFrameEdge] | AnimationKeyFrame[];
|
||||||
|
export type AnimationStyles = Record<string, any>;
|
||||||
|
|
||||||
export interface AnimationCallbackOptions {
|
export interface AnimationCallbackOptions {
|
||||||
oneTimeCallback: boolean;
|
oneTimeCallback: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AnimationKeyFrames = AnimationKeyFrame[];
|
|
||||||
|
|
||||||
export interface AnimationKeyFrame extends AnimationStyles {
|
export interface AnimationKeyFrame extends AnimationStyles {
|
||||||
offset: number;
|
offset: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AnimationStyles = Record<string, any>;
|
export interface AnimationKeyFrameEdge extends AnimationStyles {
|
||||||
|
offset?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface AnimationPlayOptions {
|
export interface AnimationPlayOptions {
|
||||||
sync: boolean;
|
sync: boolean;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { raf } from '../helpers';
|
import { raf } from '../helpers';
|
||||||
|
|
||||||
import { Animation, AnimationCallbackOptions, AnimationDirection, AnimationFill, AnimationKeyFrame, AnimationKeyFrames, AnimationLifecycle, AnimationPlayOptions } from './animation-interface';
|
import { Animation, AnimationCallbackOptions, AnimationDirection, AnimationFill, AnimationKeyFrame, AnimationKeyFrameEdge, AnimationKeyFrames, AnimationLifecycle, AnimationPlayOptions } from './animation-interface';
|
||||||
import { addClassToArray, animationEnd, createKeyframeStylesheet, generateKeyframeName, generateKeyframeRules, removeStyleProperty, setStyleProperty } from './animation-utils';
|
import { addClassToArray, animationEnd, createKeyframeStylesheet, generateKeyframeName, generateKeyframeRules, removeStyleProperty, setStyleProperty } from './animation-utils';
|
||||||
|
|
||||||
interface AnimationOnFinishCallback {
|
interface AnimationOnFinishCallback {
|
||||||
@ -32,8 +32,6 @@ export const createAnimation = (animationId?: string): Animation => {
|
|||||||
let _fill: AnimationFill | undefined;
|
let _fill: AnimationFill | undefined;
|
||||||
let _direction: AnimationDirection | undefined;
|
let _direction: AnimationDirection | undefined;
|
||||||
let _keyframes: AnimationKeyFrames = [];
|
let _keyframes: AnimationKeyFrames = [];
|
||||||
const id: string | undefined = animationId;
|
|
||||||
|
|
||||||
let beforeAddClasses: string[] = [];
|
let beforeAddClasses: string[] = [];
|
||||||
let beforeRemoveClasses: string[] = [];
|
let beforeRemoveClasses: string[] = [];
|
||||||
let initialized = false;
|
let initialized = false;
|
||||||
@ -55,6 +53,7 @@ export const createAnimation = (animationId?: string): Animation => {
|
|||||||
let keyframeName: string | undefined;
|
let keyframeName: string | undefined;
|
||||||
let ani: AnimationInternal;
|
let ani: AnimationInternal;
|
||||||
|
|
||||||
|
const id: string | undefined = animationId;
|
||||||
const onFinishCallbacks: AnimationOnFinishCallback[] = [];
|
const onFinishCallbacks: AnimationOnFinishCallback[] = [];
|
||||||
const onFinishOneTimeCallbacks: AnimationOnFinishCallback[] = [];
|
const onFinishOneTimeCallbacks: AnimationOnFinishCallback[] = [];
|
||||||
const elements: HTMLElement[] = [];
|
const elements: HTMLElement[] = [];
|
||||||
@ -402,80 +401,20 @@ export const createAnimation = (animationId?: string): Animation => {
|
|||||||
return ani;
|
return ani;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs all before read callbacks
|
|
||||||
*/
|
|
||||||
const runBeforeRead = () => {
|
|
||||||
_beforeAddReadFunctions.forEach(callback => {
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs all before write callbacks
|
|
||||||
*/
|
|
||||||
const runBeforeWrite = () => {
|
|
||||||
_beforeAddWriteFunctions.forEach(callback => {
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates styles and classes before animation runs
|
|
||||||
*/
|
|
||||||
const runBeforeStyles = () => {
|
|
||||||
const addClasses = beforeAddClasses;
|
|
||||||
const removeClasses = beforeRemoveClasses;
|
|
||||||
const styles = beforeStylesValue;
|
|
||||||
|
|
||||||
elements.forEach(el => {
|
|
||||||
const elementClassList = el.classList;
|
|
||||||
|
|
||||||
addClasses.forEach(c => elementClassList.add(c));
|
|
||||||
removeClasses.forEach(c => elementClassList.remove(c));
|
|
||||||
|
|
||||||
for (const property in styles) {
|
|
||||||
if (styles.hasOwnProperty(property)) {
|
|
||||||
setStyleProperty(el, property, styles[property]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run all "before" animation hooks.
|
* Run all "before" animation hooks.
|
||||||
*/
|
*/
|
||||||
const beforeAnimation = () => {
|
const beforeAnimation = () => {
|
||||||
runBeforeRead();
|
// Runs all before read callbacks
|
||||||
runBeforeWrite();
|
_beforeAddReadFunctions.forEach(callback => callback());
|
||||||
runBeforeStyles();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
// Runs all before write callbacks
|
||||||
* Runs all after read callbacks
|
_beforeAddWriteFunctions.forEach(callback => callback());
|
||||||
*/
|
|
||||||
const runAfterRead = () => {
|
|
||||||
_afterAddReadFunctions.forEach(callback => {
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
// Updates styles and classes before animation runs
|
||||||
* Runs all after write callbacks
|
const addClasses = beforeAddClasses;
|
||||||
*/
|
const removeClasses = beforeRemoveClasses;
|
||||||
const runAfterWrite = () => {
|
const styles = beforeStylesValue;
|
||||||
_afterAddWriteFunctions.forEach(callback => {
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates styles and classes before animation ends
|
|
||||||
*/
|
|
||||||
const runAfterStyles = () => {
|
|
||||||
const addClasses = afterAddClasses;
|
|
||||||
const removeClasses = afterRemoveClasses;
|
|
||||||
const styles = afterStylesValue;
|
|
||||||
|
|
||||||
elements.forEach(el => {
|
elements.forEach(el => {
|
||||||
const elementClassList = el.classList;
|
const elementClassList = el.classList;
|
||||||
@ -496,11 +435,31 @@ export const createAnimation = (animationId?: string): Animation => {
|
|||||||
*/
|
*/
|
||||||
const afterAnimation = () => {
|
const afterAnimation = () => {
|
||||||
clearCSSAnimationsTimeout();
|
clearCSSAnimationsTimeout();
|
||||||
runAfterRead();
|
|
||||||
runAfterWrite();
|
|
||||||
runAfterStyles();
|
|
||||||
|
|
||||||
|
// Runs all after read callbacks
|
||||||
|
_afterAddReadFunctions.forEach(callback => callback());
|
||||||
|
|
||||||
|
// Runs all after write callbacks
|
||||||
|
_afterAddWriteFunctions.forEach(callback => callback());
|
||||||
|
|
||||||
|
// Updates styles and classes before animation ends
|
||||||
const currentStep = willComplete ? 1 : 0;
|
const currentStep = willComplete ? 1 : 0;
|
||||||
|
const addClasses = afterAddClasses;
|
||||||
|
const removeClasses = afterRemoveClasses;
|
||||||
|
const styles = afterStylesValue;
|
||||||
|
|
||||||
|
elements.forEach(el => {
|
||||||
|
const elementClassList = el.classList;
|
||||||
|
|
||||||
|
addClasses.forEach(c => elementClassList.add(c));
|
||||||
|
removeClasses.forEach(c => elementClassList.remove(c));
|
||||||
|
|
||||||
|
for (const property in styles) {
|
||||||
|
if (styles.hasOwnProperty(property)) {
|
||||||
|
setStyleProperty(el, property, styles[property]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
onFinishCallbacks.forEach(onFinishCallback => {
|
onFinishCallbacks.forEach(onFinishCallback => {
|
||||||
return onFinishCallback.c(currentStep, ani);
|
return onFinishCallback.c(currentStep, ani);
|
||||||
@ -513,7 +472,10 @@ export const createAnimation = (animationId?: string): Animation => {
|
|||||||
onFinishOneTimeCallbacks.length = 0;
|
onFinishOneTimeCallbacks.length = 0;
|
||||||
|
|
||||||
shouldCalculateNumAnimations = true;
|
shouldCalculateNumAnimations = true;
|
||||||
finished = true;
|
if (willComplete) {
|
||||||
|
finished = true;
|
||||||
|
}
|
||||||
|
willComplete = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const animationFinish = () => {
|
const animationFinish = () => {
|
||||||
@ -606,12 +568,12 @@ export const createAnimation = (animationId?: string): Animation => {
|
|||||||
step = Math.min(Math.max(step, 0), 0.999);
|
step = Math.min(Math.max(step, 0), 0.999);
|
||||||
if (supportsWebAnimations) {
|
if (supportsWebAnimations) {
|
||||||
webAnimations.forEach(animation => {
|
webAnimations.forEach(animation => {
|
||||||
animation.currentTime = animation.effect.getComputedTiming().delay + (getDuration()! * step);
|
animation.currentTime = animation.effect.getComputedTiming().delay + (getDuration() * step);
|
||||||
animation.pause();
|
animation.pause();
|
||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
const animationDuration = `-${getDuration()! * step}ms`;
|
const animationDuration = `-${getDuration() * step}ms`;
|
||||||
|
|
||||||
elements.forEach(element => {
|
elements.forEach(element => {
|
||||||
if (_keyframes.length > 0) {
|
if (_keyframes.length > 0) {
|
||||||
@ -720,36 +682,45 @@ export const createAnimation = (animationId?: string): Animation => {
|
|||||||
|
|
||||||
finished = false;
|
finished = false;
|
||||||
|
|
||||||
willComplete = playTo === 1;
|
// tslint:disable-next-line: strict-boolean-conditions
|
||||||
|
willComplete = true;
|
||||||
|
|
||||||
if (!willComplete) {
|
if (playTo === 0) {
|
||||||
forceDirectionValue = (getDirection() === 'reverse') ? 'normal' : 'reverse';
|
forceDirectionValue = (getDirection() === 'reverse') ? 'normal' : 'reverse';
|
||||||
|
|
||||||
|
if (forceDirectionValue === 'reverse') {
|
||||||
|
willComplete = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (supportsWebAnimations) {
|
if (supportsWebAnimations) {
|
||||||
update();
|
update();
|
||||||
setAnimationStep(1 - step);
|
setAnimationStep(1 - step);
|
||||||
} else {
|
} else {
|
||||||
forceDelayValue = ((1 - step) * getDuration()!) * -1;
|
forceDelayValue = ((1 - step) * getDuration()) * -1;
|
||||||
update(false, false);
|
update(false, false);
|
||||||
}
|
}
|
||||||
} else {
|
} else if (playTo === 1) {
|
||||||
if (!supportsWebAnimations) {
|
if (supportsWebAnimations) {
|
||||||
forceDelayValue = (step * getDuration()!) * -1;
|
update();
|
||||||
|
setAnimationStep(step);
|
||||||
|
} else {
|
||||||
|
forceDelayValue = (step * getDuration()) * -1;
|
||||||
update(false, false);
|
update(false, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onFinish(() => {
|
if (playTo !== undefined) {
|
||||||
willComplete = true;
|
onFinish(() => {
|
||||||
forceDurationValue = undefined;
|
forceDurationValue = undefined;
|
||||||
forceDirectionValue = undefined;
|
forceDirectionValue = undefined;
|
||||||
forceDelayValue = undefined;
|
forceDelayValue = undefined;
|
||||||
}, {
|
}, {
|
||||||
oneTimeCallback: true
|
oneTimeCallback: true
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!parentAnimation) {
|
if (!parentAnimation) {
|
||||||
play();
|
play();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ani;
|
return ani;
|
||||||
@ -874,6 +845,7 @@ export const createAnimation = (animationId?: string): Animation => {
|
|||||||
const resetAnimation = () => {
|
const resetAnimation = () => {
|
||||||
if (supportsWebAnimations) {
|
if (supportsWebAnimations) {
|
||||||
setAnimationStep(0);
|
setAnimationStep(0);
|
||||||
|
updateWebAnimation();
|
||||||
} else {
|
} else {
|
||||||
updateCSSAnimation();
|
updateCSSAnimation();
|
||||||
}
|
}
|
||||||
@ -926,30 +898,30 @@ export const createAnimation = (animationId?: string): Animation => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const from = (property: string, value: any) => {
|
const from = (property: string, value: any) => {
|
||||||
const firstFrame = _keyframes[0] as AnimationKeyFrame | undefined;
|
const firstFrame = _keyframes[0] as AnimationKeyFrameEdge | undefined;
|
||||||
|
|
||||||
if (firstFrame !== undefined && firstFrame.offset === 0) {
|
if (firstFrame !== undefined && (firstFrame.offset === undefined || firstFrame.offset === 0)) {
|
||||||
firstFrame[property] = value;
|
firstFrame[property] = value;
|
||||||
} else {
|
} else {
|
||||||
_keyframes = [
|
_keyframes = [
|
||||||
{ offset: 0, [property]: value },
|
{ offset: 0, [property]: value },
|
||||||
..._keyframes
|
..._keyframes
|
||||||
];
|
] as AnimationKeyFrame[];
|
||||||
}
|
}
|
||||||
|
|
||||||
return ani;
|
return ani;
|
||||||
};
|
};
|
||||||
|
|
||||||
const to = (property: string, value: any) => {
|
const to = (property: string, value: any) => {
|
||||||
const lastFrame = _keyframes[_keyframes.length - 1] as AnimationKeyFrame | undefined;
|
const lastFrame = _keyframes[_keyframes.length - 1] as AnimationKeyFrameEdge | undefined;
|
||||||
|
|
||||||
if (lastFrame !== undefined && lastFrame.offset === 1) {
|
if (lastFrame !== undefined && (lastFrame.offset === undefined || lastFrame.offset === 1)) {
|
||||||
lastFrame[property] = value;
|
lastFrame[property] = value;
|
||||||
} else {
|
} else {
|
||||||
_keyframes = [
|
_keyframes = [
|
||||||
..._keyframes,
|
..._keyframes,
|
||||||
{ offset: 1, [property]: value }
|
{ offset: 1, [property]: value }
|
||||||
];
|
] as AnimationKeyFrame[];
|
||||||
}
|
}
|
||||||
return ani;
|
return ani;
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user