diff --git a/docs/utilities/animations.md b/docs/utilities/animations.md index a71aeee59c2..87b67c482b7 100644 --- a/docs/utilities/animations.md +++ b/docs/utilities/animations.md @@ -275,323 +275,11 @@ import Chain from '@site/static/usage/v7/animations/chain/index.md'; Ionic Animations gives developers the ability to create powerful gesture-based animations by integrating seamlessly with [Ionic Gestures](gestures.md). -### Usage - -````mdx-code-block - - - -```javascript -let initialStep = 0; -let started = false; - -const square = document.querySelector('.square'); -const MAX_TRANSLATE = 400; - -const animation = createAnimation() - .addElement(square) - .duration(1000) - .fromTo('transform', 'translateX(0)', `translateX(${MAX_TRANSLATE}px)`); - -const gesture = createGesture({ - el: square, - threshold: 0, - gestureName: 'square-drag', - onMove: ev => onMove(ev), - onEnd: ev => onEnd(ev) -}) - -gesture.enable(true); - -const onMove = (ev): { - if (!started) { - 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; -} - -const clamp = (min, n, max): { - return Math.max(min, Math.min(n, max)); -}; - -const getStep = (ev): { - const delta = initialStep + ev.deltaX; - return clamp(0, delta / MAX_TRANSLATE, 1); -} -``` - - - -```tsx -private animation?: Animation; -private gesture?: Gesture; - -private started: boolean = false; -private initialStep: number = 0; -private MAX_TRANSLATE: number = 400; - -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, - gestureName: 'square-drag', - onMove: ev => this.onMove(ev), - onEnd: ev => this.onEnd(ev) - }) - - this.gesture.enable(true); -} - -private onMove(ev) { - if (!started) { - 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; -} - -private clamp(min, n, max) { - return Math.max(min, Math.min(n, max)); -} - -private getStep(ev) { - const delta = this.initialStep + ev.deltaX; - return this.clamp(0, delta / this.MAX_TRANSLATE, 1); -} -``` - - - -```javascript -import { createGesture, CreateAnimation, Gesture, GestureDetail } from '@ionic/react'; -import React from 'react'; - -const MAX_TRANSLATE = 400; - -class MyComponent extends React.Component<{}, any> { - private animation: React.RefObject = React.createRef(); - private gesture?: Gesture; - private started: boolean = false; - private initialStep: number = 0; - - constructor(props: any) { - super(props); - - this.state = { - progressStart: undefined, - progressStep: undefined, - progressEnd: undefined, - onFinish: undefined - }; - } - - componentDidMount() { - const square = Array.from(this.animation.current!.nodes.values())[0]; - - this.gesture = createGesture({ - el: square, - gestureName: 'square-drag', - threshold: 0, - onMove: ev => this.onMove(ev), - onEnd: ev => this.onEnd(ev) - }); - - this.gesture.enable(true); - } - - private onMove(ev: GestureDetail) { - if (!this.started) { - this.setState({ - ...this.state, - progressStart: { forceLinearEasing: true } - }); - 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 }, - onFinish: { callback: () => { - this.gesture!.enable(true); - this.setState({ - progressStart: undefined, - progressStep: undefined, - progressEnd: undefined - }) - }, 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 ( - <> -
- -
-
-
- - ); - } -} -``` -
- - -```javascript -import { createAnimation, createGesture } from '@ionic/vue'; -import { ref } from 'vue'; - -... - -let initialStep = 0; -let started = false; - -const squareRef = ref(); -const MAX_TRANSLATE = 400; - -const animation = createAnimation() - .addElement(squareRef.value) - .duration(1000) - .fromTo('transform', 'translateX(0)', `translateX(${MAX_TRANSLATE}px)`); - -const gesture = createGesture({ - el: squareRef.value, - threshold: 0, - gestureName: 'square-drag', - onMove: ev => onMove(ev), - onEnd: ev => onEnd(ev) -}) - -gesture.enable(true); - -const onMove = (ev): { - if (!started) { - 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; -} - -const clamp = (min, n, max): { - return Math.max(min, Math.min(n, max)); -}; - -const getStep = (ev): { - const delta = initialStep + ev.deltaX; - return clamp(0, delta / MAX_TRANSLATE, 1); -} -``` - -
-```` +In the following example we are creating a track along which we can drag the card element. Our `animation` object will take care of moving the card element either left or right, and our `gesture` object will instruct the `animation` object which direction to move in. -In this example we are creating a track along which we can drag the `.square` element. Our `animation` object will take care of moving the `.square` element either left or right, and our `gesture` object will instruct the `animation` object which direction to move in. +import Gesture from '@site/static/usage/v7/animations/gesture/index.md'; - + ## Preference-Based Animations diff --git a/static/code/stackblitz/v7/html/index.ts b/static/code/stackblitz/v7/html/index.ts index c820961c828..071bc801d6c 100644 --- a/static/code/stackblitz/v7/html/index.ts +++ b/static/code/stackblitz/v7/html/index.ts @@ -1,6 +1,6 @@ import { defineCustomElements } from '@ionic/core/loader'; -import { createAnimation, loadingController, modalController, pickerController, toastController } from '@ionic/core'; +import { createAnimation, createGesture, loadingController, modalController, pickerController, toastController } from '@ionic/core'; /* Core CSS required for Ionic components to work properly */ import '@ionic/core/css/core.css'; @@ -28,3 +28,4 @@ defineCustomElements(); (window as any).pickerController = pickerController; (window as any).toastController = toastController; (window as any).createAnimation = createAnimation; +(window as any).createGesture = createGesture; diff --git a/static/usage/v7/animations/gesture/angular/example_component_css.md b/static/usage/v7/animations/gesture/angular/example_component_css.md new file mode 100644 index 00000000000..c06ce353f7c --- /dev/null +++ b/static/usage/v7/animations/gesture/angular/example_component_css.md @@ -0,0 +1,17 @@ +```css +.container { + flex-direction: column; +} + +.track { + width: 344px; + background: var(--ion-color-medium); + padding: 16px; +} + +ion-card { + width: 100px; + box-shadow: none; + margin: 0px; +} +``` diff --git a/static/usage/v7/animations/gesture/angular/example_component_html.md b/static/usage/v7/animations/gesture/angular/example_component_html.md new file mode 100644 index 00000000000..56b2deacd70 --- /dev/null +++ b/static/usage/v7/animations/gesture/angular/example_component_html.md @@ -0,0 +1,11 @@ +```html +
+
+ + Card + +
+ +

Drag the square along the track.

+
+``` diff --git a/static/usage/v7/animations/gesture/angular/example_component_ts.md b/static/usage/v7/animations/gesture/angular/example_component_ts.md new file mode 100644 index 00000000000..7b9aadccf5e --- /dev/null +++ b/static/usage/v7/animations/gesture/angular/example_component_ts.md @@ -0,0 +1,82 @@ +```ts +import { Component, ElementRef, ViewChildren, ViewChild } from '@angular/core'; +import { AnimationController, GestureController, IonCard } from '@ionic/angular'; +import type { Animation, Gesture, GestureDetail } from '@ionic/angular'; + +@Component({ + selector: 'app-example', + templateUrl: 'example.component.html', + styleUrls: ['example.component.css'], +}) +export class ExampleComponent { + @ViewChild(IonCard, { read: ElementRef }) card: ElementRef; + + private animation: Animation; + private gesture: Gesture; + private started = false; + private initialStep = 0; + + /** + * The track is 344px wide. + * The card is 100px wide. + * We want 16px of margin on each end of the track. + */ + private readonly MAX_TRANSLATE = 344 - 100 - 32; + + constructor(private animationCtrl: AnimationController, private gestureCtrl: GestureController) {} + + private onMove(ev: GestureDetail) { + if (!this.started) { + this.animation.progressStart(); + this.started = true; + } + + this.animation.progressStep(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.animation.progressEnd(shouldComplete ? 1 : 0, step).onFinish(() => { + this.gesture.enable(true); + }); + + this.initialStep = shouldComplete ? this.MAX_TRANSLATE : 0; + this.started = false; + } + + private clamp(min: number, n: number, max: number) { + return Math.max(min, Math.min(n, max)); + } + + private getStep(ev: GestureDetail) { + const delta = this.initialStep + ev.deltaX; + return this.clamp(0, delta / this.MAX_TRANSLATE, 1); + } + + ngAfterViewInit() { + this.animation = this.animationCtrl + .create() + .addElement(this.card.nativeElement) + .duration(1000) + .fromTo('transform', 'translateX(0)', `translateX(${this.MAX_TRANSLATE}px)`); + + const gesture = (this.gesture = this.gestureCtrl.create({ + el: this.card.nativeElement, + threshold: 0, + gestureName: 'card-drag', + onMove: (ev) => this.onMove(ev), + onEnd: (ev) => this.onEnd(ev), + })); + + gesture.enable(true); + } +} +``` diff --git a/static/usage/v7/animations/gesture/demo.html b/static/usage/v7/animations/gesture/demo.html new file mode 100644 index 00000000000..88aa10c5c38 --- /dev/null +++ b/static/usage/v7/animations/gesture/demo.html @@ -0,0 +1,113 @@ + + + + + + Animation + + + + + + + + + + +
+
+ + Card + +
+ +

Drag the square along the track.

+
+ + diff --git a/static/usage/v7/animations/gesture/index.md b/static/usage/v7/animations/gesture/index.md new file mode 100644 index 00000000000..b3e53d6d8f3 --- /dev/null +++ b/static/usage/v7/animations/gesture/index.md @@ -0,0 +1,35 @@ +import Playground from '@site/src/components/global/Playground'; + +import javascript from './javascript.md'; + +import react_main_tsx from './react/main_tsx.md'; +import react_main_css from './react/main_css.md'; + +import vue from './vue.md'; + +import angular_example_component_html from './angular/example_component_html.md'; +import angular_example_component_ts from './angular/example_component_ts.md'; +import angular_example_component_css from './angular/example_component_css.md'; + + diff --git a/static/usage/v7/animations/gesture/javascript.md b/static/usage/v7/animations/gesture/javascript.md new file mode 100644 index 00000000000..3535705d5eb --- /dev/null +++ b/static/usage/v7/animations/gesture/javascript.md @@ -0,0 +1,98 @@ +```html +
+
+ + Card + +
+
+ +

Drag the square along the track.

+ + + + +``` diff --git a/static/usage/v7/animations/gesture/react/main_css.md b/static/usage/v7/animations/gesture/react/main_css.md new file mode 100644 index 00000000000..c06ce353f7c --- /dev/null +++ b/static/usage/v7/animations/gesture/react/main_css.md @@ -0,0 +1,17 @@ +```css +.container { + flex-direction: column; +} + +.track { + width: 344px; + background: var(--ion-color-medium); + padding: 16px; +} + +ion-card { + width: 100px; + box-shadow: none; + margin: 0px; +} +``` diff --git a/static/usage/v7/animations/gesture/react/main_tsx.md b/static/usage/v7/animations/gesture/react/main_tsx.md new file mode 100644 index 00000000000..73fca7dfe22 --- /dev/null +++ b/static/usage/v7/animations/gesture/react/main_tsx.md @@ -0,0 +1,92 @@ +```tsx +import React, { useEffect, useRef } from 'react'; +import { IonCard, IonCardContent, createAnimation, createGesture } from '@ionic/react'; +import type { Animation, Gesture, GestureDetail } from '@ionic/react'; + +import './main.css'; + +function Example() { + const cardEl = useRef(null); + const animation = useRef(null); + const gesture = useRef(null); + const initialStep = useRef(0); + const started = useRef(false); + + useEffect(() => { + if (animation.current === null) { + /** + * The track is 344px wide. + * The card is 100px wide. + * We want 16px of margin on each end of the track. + */ + const MAX_TRANSLATE = 344 - 100 - 32; + + animation.current = createAnimation() + .addElement(cardEl.current!) + .duration(1000) + .fromTo('transform', 'translateX(0)', `translateX(${MAX_TRANSLATE}px)`); + + gesture.current = createGesture({ + el: cardEl.current!, + threshold: 0, + gestureName: 'card-drag', + onMove: (ev) => onMove(ev), + onEnd: (ev) => onEnd(ev), + }); + + gesture.current.enable(true); + + const onMove = (ev: GestureDetail) => { + if (!started.current) { + animation.current!.progressStart(); + started.current = true; + } + + animation.current!.progressStep(getStep(ev)); + }; + + const onEnd = (ev: GestureDetail) => { + if (!started.current) { + return; + } + + gesture.current!.enable(false); + + const step = getStep(ev); + const shouldComplete = step > 0.5; + + animation.current!.progressEnd(shouldComplete ? 1 : 0, step).onFinish(() => { + gesture.current!.enable(true); + }); + + initialStep.current = shouldComplete ? MAX_TRANSLATE : 0; + started.current = false; + }; + + const clamp = (min: number, n: number, max: number) => { + return Math.max(min, Math.min(n, max)); + }; + + const getStep = (ev: GestureDetail) => { + const delta = initialStep.current + ev.deltaX; + return clamp(0, delta / MAX_TRANSLATE, 1); + }; + } + }, [cardEl]); + + return ( + <> +
+
+ + Card + +
+ +

Drag the square along the track.

+
+ + ); +} +export default Example; +``` diff --git a/static/usage/v7/animations/gesture/vue.md b/static/usage/v7/animations/gesture/vue.md new file mode 100644 index 00000000000..9555355fe48 --- /dev/null +++ b/static/usage/v7/animations/gesture/vue.md @@ -0,0 +1,112 @@ +```html + + + + + +```