diff --git a/src/pages/utilities/animations.md b/src/pages/utilities/animations.md index 43752359e4e..6e606341f8d 100755 --- a/src/pages/utilities/animations.md +++ b/src/pages/utilities/animations.md @@ -53,7 +53,7 @@ const animation: Animation = createAnimation('') -Developers using Angular should install the latest version of `@ionic/angular`. Animations can be created via the `AnimationController` dependency injection. +Developers using Angular should install the latest version of `@ionic/angular`. Animations can be created via the `AnimationController` dependency injection. ```typescript @@ -83,8 +83,8 @@ import { CreateAnimation, Animation } from '@ionic/react'; @@ -223,7 +223,7 @@ const squareA = createAnimation() { offset: 0.5, transform: 'scale(1.2) rotate(45deg)' }, { offset: 1, transform: 'scale(1) rotate(45deg)' } ]); - + const squareB = createAnimation() .addElement(document.querySelector('.square-b')) .keyframes([ @@ -231,7 +231,7 @@ const squareB = createAnimation() { offset: 0.5, transform: 'scale(1.2)', opacity: '0.3' }, { offset: 1, transform: 'scale(1)', opacity: '1' } ]); - + const squareC = createAnimation() .addElement(document.querySelector('.square-c')) .duration(5000) @@ -258,7 +258,7 @@ const squareA = this.animationCtrl.create() { offset: 0.5, transform: 'scale(1.2) rotate(45deg)' }, { offset: 1, transform: 'scale(1) rotate(45deg)' } ]); - + const squareB = this.animationCtrl.create() .addElement(this.squareB.nativeElement) .keyframes([ @@ -266,7 +266,7 @@ const squareB = this.animationCtrl.create() { offset: 0.5, transform: 'scale(1.2)', opacity: '0.3' }, { offset: 1, transform: 'scale(1)', opacity: '1' } ]); - + const squareC = this.animationCtrl.create() .addElement(this.squareC.nativeElement) .duration(5000) @@ -298,7 +298,7 @@ componentDidMount() { const squareA = this.squareARef.current!.animation; const squareB = this.squareBRef.current!.animation; const squareC = this.squareCRef.current!.animation; - + parent.addAnimation([squareA, squareB, squareC]); } @@ -310,7 +310,7 @@ render() { duration={2000} iterations={Infinity} > - +
- +
- + ```typescript -In this example, an inline opacity of 0.2 is set on the `.square` element prior to the animation starting. Once the animation finishes, the background color of the element is set to `rgba(0, 255, 0, 0.5)`, and the inline opacity is cleared. +You can view a live example of this in Angular [here](https://stackblitz.com/edit/ionic-angular-before-after-hook-animations) and in React [here](https://stackblitz.com/edit/ionic-react-before-after-hook-animations). + +In this example, an inline height of 200px is set on the `.square` element prior to the animation starting. Once the animation finishes, the width of the element is set to `40px`, and the inline width is cleared. See [Methods](#methods) for a complete list of hooks. @@ -448,29 +446,32 @@ const squareA = createAnimation() .fill('none') .duration(1000) .keyframes([ - { offset: 0, transform: 'scale(1) rotate(0)' }, - { offset: 0.5, transform: 'scale(1.2) rotate(45deg)' }, - { offset: 1, transform: 'scale(1) rotate(0)' } + { offset: 0, transform: "scale(1) rotate(0)" }, + { offset: 0.5, transform: "scale(1.2) rotate(45deg)" }, + { offset: 1, transform: "scale(1) rotate(0)" } ]); - + const squareB = createAnimation() .addElement(document.querySelector('.square-b')) .fill('none') .duration(1000) .keyframes([ - { offset: 0, transform: 'scale(1)', opacity: '1' }, - { offset: 0.5, transform: 'scale(1.2)', opacity: '0.3' }, - { offset: 1, transform: 'scale(1)', opacity: '1' } + { offset: 0, transform: "scale(1)" }, + { offset: 0.5, transform: "scale(1.2) translateY(-40px)" }, + { offset: 1, transform: "scale(1) translateY(0)" } ]); - + const squareC = createAnimation() .addElement(document.querySelector('.square-c')) .fill('none') .duration(1000) .keyframes([ - { offset: 0, transform: 'scale(1)', opacity: '0.5' }, - { offset: 0.5, transform: 'scale(0.8)', opacity: '1' }, - { offset: 1, transform: 'scale(1)', opacity: '0.5' } + { offset: 0, transform: "scale(1)" }, + { + offset: 0.5, + transform: "scale(0.8) rotate(45deg) translateZ(20px)" + }, + { offset: 1, transform: "scale(1)" } ]); await squareA.play(); @@ -482,34 +483,34 @@ await squareC.play(); ```javascript const squareA = this.animationCtrl.create() - .addElement(this.squareA.nativeElement) - .fill('none') - .duration(1000) - .keyframes([ - { offset: 0, transform: 'scale(1) rotate(0)' }, - { offset: 0.5, transform: 'scale(1.2) rotate(45deg)' }, - { offset: 1, transform: 'scale(1) rotate(0)' } - ]); - +.addElement(document.querySelector(".squareA")) +.fill("none") +.duration(1000) +.keyframes([ + { offset: 0, transform: "scale(1) rotate(0)" }, + { offset: 0.5, transform: "scale(1.2) rotate(45deg)" }, + { offset: 1, transform: "scale(1) rotate(0)" } +]); + const squareB = this.animationCtrl.create() - .addElement(this.squareB.nativeElement) - .fill('none') - .duration(1000) - .keyframes([ - { offset: 0, transform: 'scale(1)', opacity: '1' }, - { offset: 0.5, transform: 'scale(1.2)', opacity: '0.3' }, - { offset: 1, transform: 'scale(1)', opacity: '1' } - ]); - +.addElement(document.querySelector(".squareB")) +.fill("none") +.duration(1000) +.keyframes([ + { offset: 0, transform: "scale(1)" }, + { offset: 0.5, transform: "scale(1.2) translateY(-40px)" }, + { offset: 1, transform: "scale(1) translateY(0)" } +]); + const squareC = this.animationCtrl.create() - .addElement(this.squareC.nativeElement) - .fill('none') - .duration(1000) - .keyframes([ - { offset: 0, transform: 'scale(1)', opacity: '0.5' }, - { offset: 0.5, transform: 'scale(0.8)', opacity: '1' }, - { offset: 1, transform: 'scale(1)', opacity: '0.5' } - ]); +.addElement(document.querySelector(".squareC")) +.fill("none") +.duration(1000) +.keyframes([ + { offset: 0, transform: "scale(1)" }, + { offset: 0.5, transform: "scale(0.8) rotate(45deg) translateZ(20px)"}, + { offset: 1, transform: "scale(1)" } +]); await squareA.play(); await squareB.play(); @@ -529,7 +530,7 @@ async componentDidMount() { const squareA = this.squareARef.current!.animation; const squareB = this.squareBRef.current!.animation; const squareC = this.squareCRef.current!.animation; - + await squareA.play(); await squareB.play(); await squareC.play(); @@ -543,38 +544,41 @@ render() { fill="none" duration={1000} keyframes={[ - { offset: 0, transform: 'scale(1) rotate(0)' }, - { offset: 0.5, transform: 'scale(1.2) rotate(45deg)' }, - { offset: 1, transform: 'scale(1) rotate(0deg)' } + { offset: 0, transform: "scale(1) rotate(0)" }, + { offset: 0.5, transform: "scale(1.2) rotate(45deg)" }, + { offset: 1, transform: "scale(1) rotate(0)" } ]} > -
+
- + -
+
- + -
+
) @@ -583,6 +587,8 @@ render() { +You can view a live example of this in Angular [here](https://stackblitz.com/edit/ionic-angular-chained-animations) and in React [here](https://stackblitz.com/edit/ionic-react-chained-animations). + ## Gesture Animations @@ -621,22 +627,22 @@ const onMove = (ev): { animation.progressStart(); started = true; } - + animation.progressStep(getStep(ev)); } const onEnd = (ev): { if (!started) { return; } - + gesture.enable(false); - + const step = getStep(ev); const shouldComplete = step > 0.5; animation .progressEnd((shouldComplete) ? 1 : 0, step) .onFinish((): { gesture.enable(true); }); - + initialStep = (shouldComplete) ? MAX_TRANSLATE : 0; started = false; } @@ -661,12 +667,12 @@ private started: boolean = false; private initialStep: number = 0; private MAX_TRANSLATE: number = 400; -ngOnInit() { +ngOnInit() { this.animation = this.animationCtrl.create() .addElement(this.square.nativeElement) .duration(1000) .fromTo('transform', 'translateX(0)', `translateX(${this.MAX_TRANSLATE}px)`); - + this.gesture = this.gestureCtrl.create({ el: this.square.nativeElement, threshold: 0, @@ -674,7 +680,7 @@ ngOnInit() { onMove: ev: this.onMove(ev), onEnd: ev: this.onEnd(ev) }) - + this.gesture.enable(true); } @@ -683,22 +689,22 @@ private onMove(ev) { this.animation.progressStart(); this.started = true; } - + this.animation.progressStep(this.getStep(ev)); } private onEnd(ev) { if (!this.started) { return; } - + this.gesture.enable(false); - + const step = this.getStep(ev); const shouldComplete = step > 0.5; this.animation .progressEnd((shouldComplete) ? 1 : 0, step) .onFinish((): { this.gesture.enable(true); }); - + this.initialStep = (shouldComplete) ? this.MAX_TRANSLATE : 0; this.started = false; } @@ -726,10 +732,10 @@ class MyComponent extends React.Component<{}, any> { private gesture?: Gesture; private started: boolean = false; private initialStep: number = 0; - + constructor(props: any) { super(props); - + this.state = { progressStart: undefined, progressStep: undefined, @@ -737,10 +743,10 @@ class MyComponent extends React.Component<{}, any> { onFinish: undefined }; } - + componentDidMount() { const square = Array.from(this.animation.current!.nodes.values())[0]; - + this.gesture = createGesture({ el: square, gestureName: 'square-drag', @@ -748,11 +754,11 @@ class MyComponent extends React.Component<{}, any> { onMove: ev => this.onMove(ev), onEnd: ev => this.onEnd(ev) }); - + this.gesture.enable(true); } - - private onMove(ev: GestureDetail) { + + private onMove(ev: GestureDetail) { if (!this.started) { this.setState({ ...this.state, @@ -760,21 +766,21 @@ class MyComponent extends React.Component<{}, any> { }); this.started = true; } - + this.setState({ ...this.state, progressStep: { step: this.getStep(ev) } }); } - + private onEnd(ev: GestureDetail) { if (!this.started) { return; } - + this.gesture!.enable(false); - + const step = this.getStep(ev); const shouldComplete = step > 0.5; - + this.setState({ ...this.state, progressEnd: { playTo: (shouldComplete) ? 1 : 0, step }, @@ -787,20 +793,20 @@ class MyComponent extends React.Component<{}, any> { }) }, opts: { oneTimeCallback: true }} }); - + this.initialStep = (shouldComplete) ? MAX_TRANSLATE : 0; this.started = false; } - + private getStep(ev: GestureDetail) { const delta = this.initialStep + ev.deltaX; return this.clamp(0, delta / MAX_TRANSLATE, 1); } - + private clamp(min: number, n: number, max: number) { return Math.max(min, Math.min(n, max)); } - + render() { return ( <> @@ -845,7 +851,7 @@ Developers can also tailor their animations to user preferences such as `prefers opacity: 0.5; background: blue; margin: 10px; - + --background: red; } @@ -934,25 +940,25 @@ function presentModal() { const backdropAnimation = createAnimation() .addElement(baseEl.querySelector('ion-backdrop')!) .fromTo('opacity', '0.01', 'var(--backdrop-opacity)'); - + const wrapperAnimation = createAnimation() .addElement(baseEl.querySelector('.modal-wrapper')!) .keyframes([ { offset: 0, opacity: '0', transform: 'scale(0)' }, { offset: 1, opacity: '0.99', transform: 'scale(1)' } ]); - + return createAnimation() .addElement(baseEl) .easing('ease-out') .duration(500) .addAnimation([backdropAnimation, wrapperAnimation]); } - + const leaveAnimation = (baseEl: any) => { return enterAnimation(baseEl).direction('reverse'); } - + // create the modal with the `modal-page` component const modalElement = document.createElement('ion-modal'); modalElement.component = 'modal-page'; @@ -986,25 +992,25 @@ export class ModalExample { const backdropAnimation = this.animationCtrl.create() .addElement(baseEl.querySelector('ion-backdrop')!) .fromTo('opacity', '0.01', 'var(--backdrop-opacity)'); - + const wrapperAnimation = this.animationCtrl.create() .addElement(baseEl.querySelector('.modal-wrapper')!) .keyframes([ { offset: 0, opacity: '0', transform: 'scale(0)' }, { offset: 1, opacity: '0.99', transform: 'scale(1)' } ]); - + return this.animationCtrl.create() .addElement(baseEl) .easing('ease-out') .duration(500) .addAnimation([backdropAnimation, wrapperAnimation]); } - + const leaveAnimation = (baseEl: any) => { return enterAnimation(baseEl).direction('reverse'); } - + const modal = await this.modalController.create({ component: ModalPage, enterAnimation, @@ -1023,26 +1029,26 @@ import { CreateAnimation, IonModal, IonButton, IonContent } from '@ionic/react'; export const ModalExample: React.FC = () => { const [showModal, setShowModal] = useState(false); - + const enterAnimation = (baseEl: any) => { const backdropAnimation = createAnimation() .addElement(baseEl.querySelector('ion-backdrop')!) .fromTo('opacity', '0.01', 'var(--backdrop-opacity)'); - + const wrapperAnimation = createAnimation() .addElement(baseEl.querySelector('.modal-wrapper')!) .keyframes([ { offset: 0, opacity: '0', transform: 'scale(0)' }, { offset: 1, opacity: '0.99', transform: 'scale(1)' } ]); - + return createAnimation() .addElement(baseEl) .easing('ease-out') .duration(500) .addAnimation([backdropAnimation, wrapperAnimation]); } - + const leaveAnimation = (baseEl: any) => { return enterAnimation(baseEl).direction('reverse'); } @@ -1094,12 +1100,12 @@ const animation = createAnimation('my-animation-identifier') | Browser/Platform | Supported Versions | | -------------------- | ------------------ | | **Chrome** | 43+ | -| **Safari** | 9+ | -| **Firefox** | 32+ | -| **IE/Edge** | 11+ | +| **Safari** | 9+ | +| **Firefox** | 32+ | +| **IE/Edge** | 11+ | | **Opera** | 30+ | | **iOS** | 9+ | -| **Android** | 5+ | +| **Android** | 5+ | > Due to a bug in Safari versions 9-11, stepping through animations via `progressStep` is not supported. This is supported on Safari 12+. @@ -1108,7 +1114,7 @@ const animation = createAnimation('my-animation-identifier') | Name | Value | | ---------------------| ------------------------------------------------------------- | | `AnimationDirection` | `'normal' \| 'reverse' \| 'alternate' \| 'alternate-reverse'` | -| `AnimationFill` | `'auto' \| 'none' \| 'forwards' \| 'backwards' \| 'both'` | +| `AnimationFill` | `'auto' \| 'none' \| 'forwards' \| 'backwards' \| 'both'` | ## Interfaces @@ -1122,10 +1128,10 @@ interface AnimationCallbackOptions { interface AnimationPlayOptions { /** - * If true, the animation will play synchronously. + * If true, the animation will play synchronously. * This is the equivalent of running the animation * with a duration of 0ms. - */ + */ sync: boolean; } ``` @@ -1135,7 +1141,7 @@ interface AnimationPlayOptions { | Name | Description | | ------------------------------------------| ------------------------------------------------- | | `childAnimations: Animation[]` | All child animations of a given parent animation. | -| `elements: HTMLElement[]` | All elements attached to an animation. | +| `elements: HTMLElement[]` | All elements attached to an animation. | | `parentAnimation?: Animation` | The parent animation of a given animation object. | ## Methods @@ -1143,7 +1149,7 @@ interface AnimationPlayOptions { | Name | Description | | ------------------------------------------| ------------------------------------------------- | | `addAnimation(animationToAdd: Animation \| Animation[]): Animation` | Group one or more animations together to be controlled by a parent animation. | -| `addElement(el: Element \| Element[] \| Node \| Node[] \| NodeList): Animation` | Add one or more elements to the animation. | +| `addElement(el: Element \| Element[] \| Node \| Node[] \| NodeList): Animation` | Add one or more elements to the animation. | | `afterAddClass(className: string \| string[]): Animation` | Add a class or array of classes to be added to all elements in an animation after the animation ends. | | `afterAddRead(readFn: (): void): Animation` | Add a function that performs a DOM read to be run after the animation ends. | | `afterAddWrite(writeFn: (): void): Animation` | Add a function that performs a DOM write to be run after the animation ends. |