From 45a59d13cc1494e989c4b61ccea94492488f3a95 Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Tue, 20 Aug 2019 19:03:17 -0400 Subject: [PATCH] perf(animation): reduce flickering when updating css animation on lower end devices (#19138) * fix flickering on lower end devices * fix flickering, reduce timeout padding * Add raf for clean up * bug fix * ensure animations are cleaned up * Revert "ensure animations are cleaned up" Thie reverts commit cf363f00be5567ba346d68659211087c8848d0d4. * fix dependnecy * Revert "ensure animations are cleaned up" merge This reverts commit cf363f00be5567ba346d68659211087c8848d0d4. --- core/src/components/menu/menu.tsx | 2 +- core/src/utils/animation/animation.ts | 83 +++++++++++++++------------ 2 files changed, 48 insertions(+), 37 deletions(-) diff --git a/core/src/components/menu/menu.tsx b/core/src/components/menu/menu.tsx index a5127ed4a6..6a58f4ddd9 100644 --- a/core/src/components/menu/menu.tsx +++ b/core/src/components/menu/menu.tsx @@ -415,7 +415,7 @@ export class Menu implements ComponentInterface, MenuI { let realDur = 0; if (missingDistance > 5) { const dur = missingDistance / Math.abs(velocity); - realDur = Math.min(dur, 300); + realDur = Math.max(dur, 300); } this.lastOnEnd = detail.timeStamp; diff --git a/core/src/utils/animation/animation.ts b/core/src/utils/animation/animation.ts index bf91bad824..dd1d8337f5 100644 --- a/core/src/utils/animation/animation.ts +++ b/core/src/utils/animation/animation.ts @@ -43,7 +43,7 @@ export const createAnimation = () => { const _afterAddWriteFunctions: any[] = []; const webAnimations: any[] = []; const supportsWebAnimations = (typeof (window as any).Animation === 'function'); - const ANIMATION_END_FALLBACK_PADDING_MS = 400; + const ANIMATION_END_FALLBACK_PADDING_MS = 100; /** * Returns the raw Web Animations object @@ -713,26 +713,28 @@ export const createAnimation = () => { const updateCSSAnimation = (toggleAnimationName = true) => { elements.forEach(element => { - setStyleProperty(element, 'animation-name', keyframeName || null); - setStyleProperty(element, 'animation-duration', (getDuration() !== undefined) ? `${getDuration()}ms` : null); - setStyleProperty(element, 'animation-timing-function', getEasing() || null); - setStyleProperty(element, 'animation-delay', (getDelay() !== undefined) ? `${getDelay()}ms` : null); - setStyleProperty(element, 'animation-fill-mode', getFill() || null); - setStyleProperty(element, 'animation-direction', getDirection() || null); - - const iterationsCount = - (getIterations() !== undefined) ? - (getIterations() === Infinity) ? 'infinite' : getIterations()!.toString() - : null; - - setStyleProperty(element, 'animation-iteration-count', iterationsCount); - - if (toggleAnimationName) { - setStyleProperty(element, 'animation-name', `${keyframeName}-alt`); - } - requestAnimationFrame(() => { setStyleProperty(element, 'animation-name', keyframeName || null); + setStyleProperty(element, 'animation-duration', (getDuration() !== undefined) ? `${getDuration()}ms` : null); + setStyleProperty(element, 'animation-timing-function', getEasing() || null); + setStyleProperty(element, 'animation-delay', (getDelay() !== undefined) ? `${getDelay()}ms` : null); + setStyleProperty(element, 'animation-fill-mode', getFill() || null); + setStyleProperty(element, 'animation-direction', getDirection() || null); + + const iterationsCount = + (getIterations() !== undefined) ? + (getIterations() === Infinity) ? 'infinite' : getIterations()!.toString() + : null; + + setStyleProperty(element, 'animation-iteration-count', iterationsCount); + + if (toggleAnimationName) { + setStyleProperty(element, 'animation-name', `${keyframeName}-alt`); + } + + requestAnimationFrame(() => { + setStyleProperty(element, 'animation-name', keyframeName || null); + }); }); }); }; @@ -905,7 +907,9 @@ export const createAnimation = () => { elements.forEach(element => { if (_keyframes.length > 0) { - setStyleProperty(element, 'animation-play-state', 'running'); + requestAnimationFrame(() => { + setStyleProperty(element, 'animation-play-state', 'running'); + }); } }); @@ -927,19 +931,33 @@ export const createAnimation = () => { animationEnd(elements[0], () => { clearCSSAnimationsTimeout(); - clearCSSAnimationPlayState(); - animationFinish(); + + /** + * Ensure that clean up + * is always done a frame + * before the onFinish handlers + * are fired. Otherwise, there + * may be flickering if a new + * animation is started on the same + * element too quickly + * + * TODO: Is there a cleaner way to do this? + */ + requestAnimationFrame(() => { + clearCSSAnimationPlayState(); + requestAnimationFrame(() => { + animationFinish(); + }); + }); }); } }; const clearCSSAnimationPlayState = () => { elements.forEach(element => { - requestAnimationFrame(() => { - removeStyleProperty(element, 'animation-duration'); - removeStyleProperty(element, 'animation-delay'); - removeStyleProperty(element, 'animation-play-state'); - }); + removeStyleProperty(element, 'animation-duration'); + removeStyleProperty(element, 'animation-delay'); + removeStyleProperty(element, 'animation-play-state'); }); }; @@ -953,18 +971,11 @@ export const createAnimation = () => { } }; - const resetCSSAnimations = () => { - elements.forEach(element => { - const newKeyframeName = (keyframeName !== undefined) ? `${keyframeName}-alt` : null; - setStyleProperty(element, 'animation-name', newKeyframeName); - }); - }; - const resetAnimation = () => { if (supportsWebAnimations) { setAnimationStep(0); } else { - resetCSSAnimations(); + updateCSSAnimation(); } }; @@ -973,7 +984,7 @@ export const createAnimation = () => { */ const play = () => { if (!initialized) { - initializeAnimation(false); + initializeAnimation(); } if (finished) {