Skip to content
Permalink
Browse files

feat(animation): add animation utility (#18918)

* Add new keyframes proof of concept

* update esm import

* add base before and after methods, add tests

* add base before and after hooks

* update clean up methods, add tests

* add web animations support, change to arrow functions

* remove console logs

* add from, to, fromTo, and other properties

* add more tests, fix onFinish functionality, being testing with nav transitions

* add progress methods, use force linear

* run linter

* Add playSync

* integrate animations with framework components

* onFinish now supports multiple callbacks

* change const to let

* testing reverse

* add support for both animation utilities

* bug fix

* export createAnimation, a few tweaks

* add base tests

* fix issue with onFinish being called out of order. added tests

* fix race conditions in tests

* clean up

* fix bug where onFinish not calling for empty elements array,  update test

* clean up

* fix treeshaking, remove old comments

* remove old tests

* Add test for animationbuilder backwards compat

* update typings for menu controller

* mock web animations in tests

* run build

* fix type errors

* sync with master

* use requestAnimationFrame instead of writeTask

* fix flaky tests, fix menu

* fix ordering

* update webdriver

* fix wrong version

* Revert "fix wrong version"

This reverts commit be91296.

Revert chromedriver update

* Revert "update webdriver"

This reverts commit e49bc9d.

Revert chromedriver update

* expose raw animation object, add tests

* add stylesheet recycling

* finalize before and after hook tests

* a few styling changes

* fix lint warnings

* get rid of old code

* Fix progressStep overflow bug

* disable reuse stylesheet

* small updates

* fix old animation create

* setStyleProperty helper

* reuse keyframe styles

* keyframes

* fix css animation issue with display: none, add tests

* add comment

* fix issue with progress animations and css animations

* clean up

* clean up pt2

* fix tests

* fix linter

* add fill for overlays

* fix swipe to go back

* clean up css animations when done

* fix edge cases with css animations

* fix menu open and close

* add reset function

* clean up reset fn

* Fix issue where animation always being reset

* allow updating animations on the fly

* add clear onfinish method

* fix linter

* add callback options, expand force direction

* ensure opts is defined

* fix css animations open and close for menus

* remove test

* add extra check

* clean up

* fix css anim bug swipe to go back

* fix pause

* setup alt animation to avoid flickering

* clean up

* reset flags on destroy

* add ability to change duration on progressEnd

* fix flicker on duration change for css animations

* fix ios transition

* remove unneeded recursion

* increase durability of updating css animations on the fly

* fix gesture anim

* fix web anim as well. more work for cleanup

* simplify progressEnd for css animations

* fix swipe to go back race condition

* clean up

* Add todo

* fix one more bug
  • Loading branch information...
liamdebeasi committed Aug 12, 2019
1 parent e33cf85 commit 30ca46ab127b3cf5a0d05c54519be29dd6b4d4ab
Showing with 3,966 additions and 754 deletions.
  1. +1 βˆ’1 core/api.txt
  2. +3 βˆ’1 core/src/components.d.ts
  3. +2 βˆ’2 core/src/components/action-sheet/action-sheet.tsx
  4. +14 βˆ’16 core/src/components/action-sheet/animations/ios.enter.ts
  5. +14 βˆ’16 core/src/components/action-sheet/animations/ios.leave.ts
  6. +14 βˆ’16 core/src/components/action-sheet/animations/md.enter.ts
  7. +14 βˆ’16 core/src/components/action-sheet/animations/md.leave.ts
  8. +17 βˆ’16 core/src/components/alert/animations/ios.enter.ts
  9. +17 βˆ’16 core/src/components/alert/animations/ios.leave.ts
  10. +17 βˆ’14 core/src/components/alert/animations/md.enter.ts
  11. +14 βˆ’14 core/src/components/alert/animations/md.leave.ts
  12. +17 βˆ’15 core/src/components/loading/animations/ios.enter.ts
  13. +17 βˆ’15 core/src/components/loading/animations/ios.leave.ts
  14. +17 βˆ’14 core/src/components/loading/animations/md.enter.ts
  15. +17 βˆ’14 core/src/components/loading/animations/md.leave.ts
  16. +5 βˆ’6 core/src/components/menu-controller/animations/base.ts
  17. +12 βˆ’10 core/src/components/menu-controller/animations/overlay.ts
  18. +11 βˆ’13 core/src/components/menu-controller/animations/push.ts
  19. +6 βˆ’7 core/src/components/menu-controller/animations/reveal.ts
  20. +8 βˆ’7 core/src/components/menu-controller/menu-controller.ts
  21. +1 βˆ’1 core/src/components/menu-controller/readme.md
  22. +17 βˆ’8 core/src/components/menu/menu.tsx
  23. +6 βˆ’1 core/src/components/menu/test/basic/index.html
  24. +15 βˆ’15 core/src/components/modal/animations/ios.enter.ts
  25. +15 βˆ’15 core/src/components/modal/animations/ios.leave.ts
  26. +16 βˆ’15 core/src/components/modal/animations/md.enter.ts
  27. +17 βˆ’16 core/src/components/modal/animations/md.leave.ts
  28. +3 βˆ’3 core/src/components/nav/nav.tsx
  29. +26 βˆ’1 core/src/components/nav/test/nav-controller.spec.ts
  30. +14 βˆ’14 core/src/components/picker/animations/ios.enter.ts
  31. +14 βˆ’14 core/src/components/picker/animations/ios.leave.ts
  32. +14 βˆ’12 core/src/components/popover/animations/ios.enter.ts
  33. +14 βˆ’13 core/src/components/popover/animations/ios.leave.ts
  34. +33 βˆ’31 core/src/components/popover/animations/md.enter.ts
  35. +14 βˆ’13 core/src/components/popover/animations/md.leave.ts
  36. +2 βˆ’2 core/src/components/router-outlet/route-outlet.tsx
  37. +11 βˆ’11 core/src/components/toast/animations/ios.enter.ts
  38. +11 βˆ’11 core/src/components/toast/animations/ios.leave.ts
  39. +9 βˆ’9 core/src/components/toast/animations/md.enter.ts
  40. +10 βˆ’10 core/src/components/toast/animations/md.leave.ts
  41. +3 βˆ’0 core/src/index.ts
  42. +2 βˆ’1 core/src/interface.d.ts
  43. +61 βˆ’54 core/src/utils/animation/animation-interface.ts
  44. +95 βˆ’0 core/src/utils/animation/animation-utils.ts
  45. +1,126 βˆ’0 core/src/utils/animation/animation.ts
  46. +64 βˆ’0 core/src/utils/animation/old-animation/animation-interface.ts
  47. 0 core/src/utils/animation/{ β†’ old-animation}/animator.ts
  48. +2 βˆ’2 core/src/utils/animation/{ β†’ old-animation}/index.ts
  49. 0 core/src/utils/animation/{ β†’ old-animation}/transition-end.ts
  50. +246 βˆ’0 core/src/utils/animation/test/animation.spec.ts
  51. +65 βˆ’0 core/src/utils/animation/test/animationbuilder/e2e.ts
  52. +190 βˆ’0 core/src/utils/animation/test/animationbuilder/index.html
  53. +53 βˆ’0 core/src/utils/animation/test/basic/e2e.ts
  54. +97 βˆ’0 core/src/utils/animation/test/basic/index.html
  55. +227 βˆ’0 core/src/utils/animation/test/chaining/index.html
  56. +54 βˆ’0 core/src/utils/animation/test/display/e2e.ts
  57. +131 βˆ’0 core/src/utils/animation/test/display/index.html
  58. +173 βˆ’0 core/src/utils/animation/test/gesture/index.html
  59. +144 βˆ’0 core/src/utils/animation/test/hooks/e2e.ts
  60. +121 βˆ’0 core/src/utils/animation/test/hooks/index.html
  61. +53 βˆ’0 core/src/utils/animation/test/multiple/e2e.ts
  62. +207 βˆ’0 core/src/utils/animation/test/multiple/index.html
  63. +77 βˆ’0 core/src/utils/animation/test/reuse/index.html
  64. +1 βˆ’3 core/src/utils/gesture/index.ts
  65. +2 βˆ’2 core/src/utils/overlays-interface.ts
  66. +30 βˆ’10 core/src/utils/overlays.ts
  67. +1 βˆ’1 core/src/utils/test/utils.ts
  68. +35 βˆ’14 core/src/utils/transition/index.ts
  69. +194 βˆ’211 core/src/utils/transition/ios.transition.ts
  70. +13 βˆ’12 core/src/utils/transition/md.transition.ts
@@ -663,7 +663,7 @@ ion-menu-controller,method,isAnimating,isAnimating() => Promise<boolean>
ion-menu-controller,method,isEnabled,isEnabled(menu?: string | null | undefined) => Promise<boolean>
ion-menu-controller,method,isOpen,isOpen(menu?: string | null | undefined) => Promise<boolean>
ion-menu-controller,method,open,open(menu?: string | null | undefined) => Promise<boolean>
ion-menu-controller,method,registerAnimation,registerAnimation(name: string, animation: AnimationBuilder) => Promise<void>
ion-menu-controller,method,registerAnimation,registerAnimation(name: string, animation: AnimationBuilder | ((menu: MenuI) => IonicAnimation)) => Promise<void>
ion-menu-controller,method,swipeGesture,swipeGesture(enable: boolean, menu?: string | null | undefined) => Promise<HTMLIonMenuElement | undefined>
ion-menu-controller,method,toggle,toggle(menu?: string | null | undefined) => Promise<boolean>

@@ -25,12 +25,14 @@ import {
HeaderFn,
HeaderHeightFn,
InputChangeEventDetail,
IonicAnimation,
ItemHeightFn,
ItemRenderFn,
ItemReorderEventDetail,
LoadingOptions,
MenuChangeEventDetail,
MenuControllerI,
MenuI,
ModalOptions,
NavComponent,
NavOptions,
@@ -1425,7 +1427,7 @@ export namespace Components {
* @param name The name of the animation to register.
* @param animation The animation function to register.
*/
'registerAnimation': (name: string, animation: AnimationBuilder) => Promise<void>;
'registerAnimation': (name: string, animation: AnimationBuilder | ((menu: MenuI) => IonicAnimation)) => Promise<void>;
/**
* Enable or disable the ability to swipe open the menu.
* @param enable If `true`, the menu swipe gesture should be enabled.
@@ -1,7 +1,7 @@
import { Component, ComponentInterface, Element, Event, EventEmitter, Host, Method, Prop, h } from '@stencil/core';

import { getIonMode } from '../../global/ionic-global';
import { ActionSheetButton, Animation, AnimationBuilder, CssClassMap, OverlayEventDetail, OverlayInterface } from '../../interface';
import { ActionSheetButton, AnimationBuilder, CssClassMap, OverlayEventDetail, OverlayInterface } from '../../interface';
import { BACKDROP, dismiss, eventMethod, isCancel, present, safeCall } from '../../utils/overlays';
import { getClassMap } from '../../utils/theme';

@@ -24,7 +24,7 @@ import { mdLeaveAnimation } from './animations/md.leave';
export class ActionSheet implements ComponentInterface, OverlayInterface {

presented = false;
animation?: Animation;
animation?: any;
mode = getIonMode(this);

@Element() el!: HTMLElement;
@@ -1,27 +1,25 @@
import { Animation } from '../../../interface';
import { IonicAnimation } from '../../../interface';
import { createAnimation } from '../../../utils/animation/animation';

/**
* iOS Action Sheet Enter Animation
*/
export const iosEnterAnimation = (AnimationC: Animation, baseEl: HTMLElement): Promise<Animation> => {
const baseAnimation = new AnimationC();
export const iosEnterAnimation = (baseEl: HTMLElement): IonicAnimation => {
const baseAnimation = createAnimation();
const backdropAnimation = createAnimation();
const wrapperAnimation = createAnimation();

const backdropAnimation = new AnimationC();
backdropAnimation.addElement(baseEl.querySelector('ion-backdrop'));
backdropAnimation
.addElement(baseEl.querySelector('ion-backdrop'))
.fromTo('opacity', 0.01, 0.4);

const wrapperAnimation = new AnimationC();
wrapperAnimation.addElement(baseEl.querySelector('.action-sheet-wrapper'));
wrapperAnimation
.addElement(baseEl.querySelector('.action-sheet-wrapper'))
.fromTo('transform', 'translateY(100%)', 'translateY(0%)');

backdropAnimation.fromTo('opacity', 0.01, 0.4);

wrapperAnimation.fromTo('translateY', '100%', '0%');

const ani = baseAnimation
return baseAnimation
.addElement(baseEl)
.easing('cubic-bezier(.36,.66,.04,1)')
.duration(400)
.add(backdropAnimation)
.add(wrapperAnimation);

return Promise.resolve(ani);
.addAnimation([backdropAnimation, wrapperAnimation]);
};
@@ -1,27 +1,25 @@
import { Animation } from '../../../interface';
import { IonicAnimation } from '../../../interface';
import { createAnimation } from '../../../utils/animation/animation';

/**
* iOS Action Sheet Leave Animation
*/
export const iosLeaveAnimation = (AnimationC: Animation, baseEl: HTMLElement): Promise<Animation> => {
const baseAnimation = new AnimationC();
export const iosLeaveAnimation = (baseEl: HTMLElement): IonicAnimation => {
const baseAnimation = createAnimation();
const backdropAnimation = createAnimation();
const wrapperAnimation = createAnimation();

const backdropAnimation = new AnimationC();
backdropAnimation.addElement(baseEl.querySelector('ion-backdrop'));
backdropAnimation
.addElement(baseEl.querySelector('ion-backdrop'))
.fromTo('opacity', 0.4, 0);

const wrapperAnimation = new AnimationC();
wrapperAnimation.addElement(baseEl.querySelector('.action-sheet-wrapper'));
wrapperAnimation
.addElement(baseEl.querySelector('.action-sheet-wrapper'))
.fromTo('transform', 'translateY(0%)', 'translateY(100%)');

backdropAnimation.fromTo('opacity', 0.4, 0);

wrapperAnimation.fromTo('translateY', '0%', '100%');

const ani = baseAnimation
return baseAnimation
.addElement(baseEl)
.easing('cubic-bezier(.36,.66,.04,1)')
.duration(450)
.add(backdropAnimation)
.add(wrapperAnimation);

return Promise.resolve(ani);
.addAnimation([backdropAnimation, wrapperAnimation]);
};
@@ -1,27 +1,25 @@

import { Animation } from '../../../interface';
import { IonicAnimation } from '../../../interface';
import { createAnimation } from '../../../utils/animation/animation';

/**
* MD Action Sheet Enter Animation
*/
export const mdEnterAnimation = (AnimationC: Animation, baseEl: HTMLElement): Promise<Animation> => {
const baseAnimation = new AnimationC();

const backdropAnimation = new AnimationC();
backdropAnimation.addElement(baseEl.querySelector('ion-backdrop'));
export const mdEnterAnimation = (baseEl: HTMLElement): IonicAnimation => {
const baseAnimation = createAnimation();
const backdropAnimation = createAnimation();
const wrapperAnimation = createAnimation();

const wrapperAnimation = new AnimationC();
wrapperAnimation.addElement(baseEl.querySelector('.action-sheet-wrapper'));
backdropAnimation
.addElement(baseEl.querySelector('ion-backdrop'))
.fromTo('opacity', 0.01, 0.32);

backdropAnimation.fromTo('opacity', 0.01, 0.32);
wrapperAnimation.fromTo('translateY', '100%', '0%');
wrapperAnimation
.addElement(baseEl.querySelector('.action-sheet-wrapper'))
.fromTo('transform', 'translateY(100%)', 'translateY(0%)');

const ani = baseAnimation
return baseAnimation
.addElement(baseEl)
.easing('cubic-bezier(.36,.66,.04,1)')
.duration(400)
.add(backdropAnimation)
.add(wrapperAnimation);

return Promise.resolve(ani);
.addAnimation([backdropAnimation, wrapperAnimation]);
};
@@ -1,27 +1,25 @@
import { Animation } from '../../../interface';
import { IonicAnimation } from '../../../interface';
import { createAnimation } from '../../../utils/animation/animation';

/**
* MD Action Sheet Leave Animation
*/
export const mdLeaveAnimation = (AnimationC: Animation, baseEl: HTMLElement): Promise<Animation> => {
export const mdLeaveAnimation = (baseEl: HTMLElement): IonicAnimation => {
const baseAnimation = createAnimation();
const backdropAnimation = createAnimation();
const wrapperAnimation = createAnimation();

const baseAnimation = new AnimationC();
backdropAnimation
.addElement(baseEl.querySelector('ion-backdrop'))
.fromTo('opacity', 0.32, 0);

const backdropAnimation = new AnimationC();
backdropAnimation.addElement(baseEl.querySelector('ion-backdrop'));
wrapperAnimation
.addElement(baseEl.querySelector('.action-sheet-wrapper'))
.fromTo('transform', 'translateY(0%)', 'translateY(100%)');

const wrapperAnimation = new AnimationC();
wrapperAnimation.addElement(baseEl.querySelector('.action-sheet-wrapper'));

backdropAnimation.fromTo('opacity', 0.32, 0);
wrapperAnimation.fromTo('translateY', '0%', '100%');

const ani = baseAnimation
return baseAnimation
.addElement(baseEl)
.easing('cubic-bezier(.36,.66,.04,1)')
.duration(450)
.add(backdropAnimation)
.add(wrapperAnimation);

return Promise.resolve(ani);
.addAnimation([backdropAnimation, wrapperAnimation]);
};
@@ -1,27 +1,28 @@
import { Animation } from '../../../interface';
import { IonicAnimation } from '../../../interface';
import { createAnimation } from '../../../utils/animation/animation';

/**
* iOS Alert Enter Animation
*/
export const iosEnterAnimation = (AnimationC: Animation, baseEl: HTMLElement): Promise<Animation> => {
const baseAnimation = new AnimationC();
export const iosEnterAnimation = (baseEl: HTMLElement): IonicAnimation => {
const baseAnimation = createAnimation();
const backdropAnimation = createAnimation();
const wrapperAnimation = createAnimation();

const backdropAnimation = new AnimationC();
backdropAnimation.addElement(baseEl.querySelector('ion-backdrop'));
backdropAnimation
.addElement(baseEl.querySelector('ion-backdrop'))
.fromTo('opacity', 0.01, 0.3);

const wrapperAnimation = new AnimationC();
wrapperAnimation.addElement(baseEl.querySelector('.alert-wrapper'));
wrapperAnimation
.addElement(baseEl.querySelector('.alert-wrapper'))
.keyframes([
{ offset: 0, opacity: 0.01, transform: 'scale(1.1)' },
{ offset: 1, opacity: 1, transform: 'scale(1)' }
]);

backdropAnimation.fromTo('opacity', 0.01, 0.3);

wrapperAnimation.fromTo('opacity', 0.01, 1).fromTo('scale', 1.1, 1);

const ani = baseAnimation
return baseAnimation
.addElement(baseEl)
.easing('ease-in-out')
.duration(200)
.add(backdropAnimation)
.add(wrapperAnimation);

return Promise.resolve(ani);
.addAnimation([backdropAnimation, wrapperAnimation]);
};
@@ -1,27 +1,28 @@
import { Animation } from '../../../interface';
import { IonicAnimation } from '../../../interface';
import { createAnimation } from '../../../utils/animation/animation';

/**
* iOS Alert Leave Animation
*/
export const iosLeaveAnimation = (AnimationC: Animation, baseEl: HTMLElement): Promise<Animation> => {
const baseAnimation = new AnimationC();
export const iosLeaveAnimation = (baseEl: HTMLElement): IonicAnimation => {
const baseAnimation = createAnimation();
const backdropAnimation = createAnimation();
const wrapperAnimation = createAnimation();

const backdropAnimation = new AnimationC();
backdropAnimation.addElement(baseEl.querySelector('ion-backdrop'));
backdropAnimation
.addElement(baseEl.querySelector('ion-backdrop'))
.fromTo('opacity', 0.3, 0);

const wrapperAnimation = new AnimationC();
wrapperAnimation.addElement(baseEl.querySelector('.alert-wrapper'));
wrapperAnimation
.addElement(baseEl.querySelector('.alert-wrapper'))
.keyframes([
{ offset: 0, opacity: 0.99, transform: 'scale(1)' },
{ offset: 1, opacity: 0, transform: 'scale(0.9)' }
]);

backdropAnimation.fromTo('opacity', 0.3, 0);

wrapperAnimation.fromTo('opacity', 0.99, 0).fromTo('scale', 1, 0.9);

const ani = baseAnimation
return baseAnimation
.addElement(baseEl)
.easing('ease-in-out')
.duration(200)
.add(backdropAnimation)
.add(wrapperAnimation);

return Promise.resolve(ani);
.addAnimation([backdropAnimation, wrapperAnimation]);
};
@@ -1,25 +1,28 @@
import { Animation } from '../../../interface';
import { IonicAnimation } from '../../../interface';
import { createAnimation } from '../../../utils/animation/animation';

/**
* Md Alert Enter Animation
*/
export const mdEnterAnimation = (AnimationC: Animation, baseEl: HTMLElement): Promise<Animation> => {
const baseAnimation = new AnimationC();
export const mdEnterAnimation = (baseEl: HTMLElement): IonicAnimation => {
const baseAnimation = createAnimation();
const backdropAnimation = createAnimation();
const wrapperAnimation = createAnimation();

const backdropAnimation = new AnimationC();
backdropAnimation.addElement(baseEl.querySelector('ion-backdrop'));
backdropAnimation
.addElement(baseEl.querySelector('ion-backdrop'))
.fromTo('opacity', 0.01, 0.32);

const wrapperAnimation = new AnimationC();
wrapperAnimation.addElement(baseEl.querySelector('.alert-wrapper'));
wrapperAnimation
.addElement(baseEl.querySelector('.alert-wrapper'))
.keyframes([
{ offset: 0, opacity: 0.01, transform: 'scale(0.9)' },
{ offset: 1, opacity: 1, transform: 'scale(1)' }
]);

backdropAnimation.fromTo('opacity', 0.01, 0.32);

wrapperAnimation.fromTo('opacity', 0.01, 1).fromTo('scale', 0.9, 1);

return Promise.resolve(baseAnimation
return baseAnimation
.addElement(baseEl)
.easing('ease-in-out')
.duration(150)
.add(backdropAnimation)
.add(wrapperAnimation));
.addAnimation([backdropAnimation, wrapperAnimation]);
};
@@ -1,25 +1,25 @@
import { Animation } from '../../../interface';
import { IonicAnimation } from '../../../interface';
import { createAnimation } from '../../../utils/animation/animation';

/**
* Md Alert Leave Animation
*/
export const mdLeaveAnimation = (AnimationC: Animation, baseEl: HTMLElement): Promise<Animation> => {
const baseAnimation = new AnimationC();
export const mdLeaveAnimation = (baseEl: HTMLElement): IonicAnimation => {
const baseAnimation = createAnimation();
const backdropAnimation = createAnimation();
const wrapperAnimation = createAnimation();

const backdropAnimation = new AnimationC();
backdropAnimation.addElement(baseEl.querySelector('ion-backdrop'));
backdropAnimation
.addElement(baseEl.querySelector('ion-backdrop'))
.fromTo('opacity', 0.32, 0);

const wrapperAnimation = new AnimationC();
wrapperAnimation.addElement(baseEl.querySelector('.alert-wrapper'));
wrapperAnimation
.addElement(baseEl.querySelector('.alert-wrapper'))
.fromTo('opacity', 0.99, 0);

backdropAnimation.fromTo('opacity', 0.32, 0);

wrapperAnimation.fromTo('opacity', 0.99, 0);

return Promise.resolve(baseAnimation
return baseAnimation
.addElement(baseEl)
.easing('ease-in-out')
.duration(150)
.add(backdropAnimation)
.add(wrapperAnimation));
.addAnimation([backdropAnimation, wrapperAnimation]);
};

0 comments on commit 30ca46a

Please sign in to comment.
You can’t perform that action at this time.