fix(animation): properly update Web Animation, clean up types (#19964)

Co-authored-by: manucorporat <manu.mtza@gmail.com>
This commit is contained in:
Liam DeBeasi
2019-11-21 11:08:27 -05:00
committed by GitHub
parent 7734d27bc2
commit e76619478c
2 changed files with 76 additions and 102 deletions

View File

@ -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;

View File

@ -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;
}; };