From 50a92c026c365ebeaea264c32596e3f0c05c20cf Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Thu, 22 Aug 2019 16:12:17 -0400 Subject: [PATCH] fix(animation): properties can be cleared after animation finishes (#19155) * remove certain properties when done animating * fix bug, add more tests * clean up * bug fix --- .../utils/animation/animation-interface.ts | 4 +- core/src/utils/animation/animation-utils.ts | 2 +- core/src/utils/animation/animation.ts | 75 +++++++++++++-- .../utils/animation/test/animation.spec.ts | 95 +++++++++++++++++++ core/src/utils/transition/ios.transition.ts | 2 +- 5 files changed, 164 insertions(+), 14 deletions(-) diff --git a/core/src/utils/animation/animation-interface.ts b/core/src/utils/animation/animation-interface.ts index 6407c263b3..c1a578207b 100644 --- a/core/src/utils/animation/animation-interface.ts +++ b/core/src/utils/animation/animation-interface.ts @@ -16,8 +16,8 @@ export interface Animation { progressEnd(shouldComplete: boolean, step: number, dur: number | undefined): Animation; from(property: string, value: any): Animation; - to(property: string, value: any): Animation; - fromTo(property: string, fromValue: any, toValue: any): Animation; + to(property: string, value: any, clearAfterAnimation?: boolean): Animation; + fromTo(property: string, fromValue: any, toValue: any, clearAfterAnimation?: boolean): Animation; keyframes(keyframes: any[]): Animation; addAnimation(animationToADd: Animation | Animation[] | undefined | null): Animation; diff --git a/core/src/utils/animation/animation-utils.ts b/core/src/utils/animation/animation-utils.ts index 6cf773076a..98ddcea951 100644 --- a/core/src/utils/animation/animation-utils.ts +++ b/core/src/utils/animation/animation-utils.ts @@ -43,7 +43,7 @@ export const generateKeyframeRules = (keyframes: any[] = []) => { const frameString = []; for (const property in keyframe) { - if (keyframe.hasOwnProperty(property) && property !== 'offset') { + if (keyframe.hasOwnProperty(property) && property !== 'offset' && property !== 'clear') { frameString.push(`${property}: ${keyframe[property]};`); } } diff --git a/core/src/utils/animation/animation.ts b/core/src/utils/animation/animation.ts index dd1d8337f5..ef08824f1d 100644 --- a/core/src/utils/animation/animation.ts +++ b/core/src/utils/animation/animation.ts @@ -1047,29 +1047,84 @@ export const createAnimation = () => { return ani; }; - const to = (property: string, value: any) => { + const to = (property: string, value: any, clearAfterAnimation?: boolean) => { const lastFrame = _keyframes[_keyframes.length - 1]; if (lastFrame != null && (lastFrame.offset === undefined || lastFrame.offset === 1)) { + + /** + * If last frame is not the clear frame + * set the value as usual + */ + if (!lastFrame.clear) { lastFrame[property] = value; + } else { + /** + * If last frame is the keyframe then we need to + * set the value on the previous frame + */ + const secondToLastFrame = _keyframes[_keyframes.length - 2]; + secondToLastFrame[property] = value; + } + + /** + * If not on the last possible keyframe, add + * the last keyframe + */ } else { - - const object: any = { - offset: 1 - }; - object[property] = value; - _keyframes = [ ..._keyframes, - object + { offset: 1, [property]: value } ]; } + if (clearAfterAnimation) { + const lastFrame = _keyframes[_keyframes.length - 1]; + if (lastFrame != null) { + + /** + * If we are on the clear frame, just set the property + */ + if (lastFrame.clear) { + lastFrame[property] = ''; + return ani; + } + + /** + * If we are already setup for a clear frame, just mark it + * as such and set the property + */ + const secondToLastFrame = _keyframes[_keyframes.length - 2]; + if (lastFrame.offset === 1 && secondToLastFrame.offset === 0.99) { + lastFrame.clear = true; + lastFrame[property] = ''; + secondToLastFrame[property] = value; + return ani; + } + + /** + * If the last frame is not the clear frame + * and has an offset of 1, we need to move it + * back by a frame to account for the clear frame + */ + if (lastFrame.offset === 1 && secondToLastFrame.offset !== 0.99) { + lastFrame.offset = 0.99; + } + + /** + * Add a clear frame that runs immediately after + * the last frame that the user has set. This will + * allow users to clear certain properties from elements + */ + _keyframes.push({ offset: lastFrame.offset + 0.01, [property]: '', clear: true }); + } + } + return ani; }; - const fromTo = (property: string, fromValue: any, toValue: any) => { - return from(property, fromValue).to(property, toValue); + const fromTo = (property: string, fromValue: any, toValue: any, clearAfterAnimation?: boolean) => { + return from(property, fromValue).to(property, toValue, clearAfterAnimation); }; return ani = { diff --git a/core/src/utils/animation/test/animation.spec.ts b/core/src/utils/animation/test/animation.spec.ts index b10d432252..6beab72835 100644 --- a/core/src/utils/animation/test/animation.spec.ts +++ b/core/src/utils/animation/test/animation.spec.ts @@ -81,6 +81,101 @@ describe('Animation Class', () => { expect(animation.getKeyframes().length).toEqual(3); }); + + it('should set the from keyframe properly', () => { + animation + .from('opacity', 0) + .from('background', 'red') + .from('color', 'purple'); + + const keyframes = animation.getKeyframes(); + + expect(keyframes.length).toEqual(1); + expect(keyframes[0]).toEqual({ + opacity: 0, + color: 'purple', + background: 'red', + offset: 0 + }); + }); + + it('should set the to keyframe properly', () => { + animation + .to('opacity', 0) + .to('background', 'red') + .to('color', 'purple'); + + const keyframes = animation.getKeyframes(); + expect(keyframes.length).toEqual(1); + expect(keyframes[0]).toEqual({ + opacity: 0, + color: 'purple', + background: 'red', + offset: 1 + }); + }); + + it('should clear properties at the end of an animation', () => { + animation + .fromTo('opacity', 0, 1, true) + .fromTo('background', 'red', 'blue') + .fromTo('color', 'purple', 'green', true); + + const keyframes = animation.getKeyframes(); + + expect(keyframes.length).toEqual(3); + expect(keyframes[0]).toEqual({ + opacity: 0, + color: 'purple', + background: 'red', + offset: 0 + }); + + expect(keyframes[1]).toEqual({ + opacity: 1, + color: 'green', + offset: 0.99, + background: 'blue' + }); + + expect(keyframes[2]).toEqual({ + clear: true, + opacity: '', + color: '', + offset: 1 + }) + }); + + it('should mix keyframes and fromTo properly', () => { + animation + .keyframes([ + { offset: 0, background: 'red' }, + { offset: 0.99, background: 'blue' }, + { offset: 1, background: 'green' } + ]) + .fromTo('opacity', 0, 1, true) + + const keyframes = animation.getKeyframes(); + expect(keyframes.length).toEqual(3); + expect(keyframes[0]).toEqual({ + opacity: 0, + background: 'red', + offset: 0 + }); + + expect(keyframes[1]).toEqual({ + opacity: 1, + background: 'blue', + offset: 0.99 + }); + + expect(keyframes[2]).toEqual({ + opacity: '', + background: 'green', + offset: 1, + clear: true + }); + }); }); describe('Animation Config Methods', () => { diff --git a/core/src/utils/transition/ios.transition.ts b/core/src/utils/transition/ios.transition.ts index a934967ce6..d987fa0350 100644 --- a/core/src/utils/transition/ios.transition.ts +++ b/core/src/utils/transition/ios.transition.ts @@ -138,7 +138,7 @@ export const iosTransitionAnimation = (navEl: HTMLElement, opts: TransitionOptio enteringToolBarBg .beforeClearStyles([OPACITY]) - .fromTo(OPACITY, 0.01, 1); + .fromTo(OPACITY, 0.01, 1, true); // forward direction, entering page has a back button enteringBackButton.fromTo(OPACITY, 0.01, 1);