Compare commits

...

10 Commits

Author SHA1 Message Date
Liam DeBeasi
ede6289bc6 remove package lock 2020-06-05 16:08:10 -04:00
Liam DeBeasi
f077f0aac7 sync with master 2020-06-05 16:06:40 -04:00
Liam DeBeasi
9c64308f0d add generic wrapper class 2020-06-05 16:05:43 -04:00
Liam DeBeasi
795783311a clean up animations: 2020-06-05 14:55:58 -04:00
Liam DeBeasi
f3e8d4c31d typo 2020-06-05 14:04:51 -04:00
Liam DeBeasi
7fb38d1e12 add support for backdrop to fade anim 2020-06-05 14:04:04 -04:00
Liam DeBeasi
9f9ba235ee add fade and fade through animations 2020-06-05 13:54:56 -04:00
Liam DeBeasi
28c5e14434 add multi axis anim 2020-05-20 11:25:30 -04:00
Liam DeBeasi
a4be67aeb8 add axis support 2020-05-18 17:39:12 -04:00
Liam DeBeasi
94c3d481e9 Add base motion package 2020-05-15 15:57:55 -04:00
17 changed files with 445 additions and 6 deletions

View File

@@ -250,7 +250,7 @@ export class ActionSheet implements ComponentInterface, OverlayInterface {
onIonBackdropTap={this.onBackdropTap}
>
<ion-backdrop tappable={this.backdropDismiss}/>
<div class="action-sheet-wrapper" role="dialog" ref={el => this.wrapperEl = el}>
<div class="action-sheet-wrapper ion-wrapper" role="dialog" ref={el => this.wrapperEl = el}>
<div class="action-sheet-container">
<div class="action-sheet-group" ref={el => this.groupEl = el}>
{this.header !== undefined &&

View File

@@ -514,7 +514,7 @@ export class Alert implements ComponentInterface, OverlayInterface {
<ion-backdrop tappable={this.backdropDismiss}/>
<div class="alert-wrapper" ref={el => this.wrapperEl = el}>
<div class="alert-wrapper ion-wrapper" ref={el => this.wrapperEl = el}>
<div class="alert-head">
{header && <h2 id={hdrId} class="alert-title">{header}</h2>}

View File

@@ -194,7 +194,7 @@ export class Loading implements ComponentInterface, OverlayInterface {
}}
>
<ion-backdrop visible={this.showBackdrop} tappable={this.backdropDismiss} />
<div class="loading-wrapper" role="dialog">
<div class="loading-wrapper ion-wrapper" role="dialog">
{spinner && (
<div class="loading-spinner">
<ion-spinner name={spinner} aria-hidden="true" />

View File

@@ -291,7 +291,7 @@ export class Modal implements ComponentInterface, OverlayInterface {
{mode === 'ios' && <div class="modal-shadow"></div>}
<div
role="dialog"
class="modal-wrapper"
class="modal-wrapper ion-wrapper"
>
</div>
</Host>

View File

@@ -236,7 +236,7 @@ export class Picker implements ComponentInterface, OverlayInterface {
tappable={this.backdropDismiss}
>
</ion-backdrop>
<div class="picker-wrapper" role="dialog">
<div class="picker-wrapper ion-wrapper" role="dialog">
<div class="picker-toolbar">
{this.buttons.map(b => (
<div class={buttonWrapperClass(b)}>

View File

@@ -219,7 +219,7 @@ export class Popover implements ComponentInterface, OverlayInterface {
onIonBackdropTap={this.onBackdropTap}
>
<ion-backdrop tappable={this.backdropDismiss} visible={this.showBackdrop}/>
<div class="popover-wrapper">
<div class="popover-wrapper ion-wrapper">
<div class="popover-arrow"></div>
<div class="popover-content"></div>
</div>

View File

@@ -256,6 +256,7 @@ export class Toast implements ComponentInterface, OverlayInterface {
const mode = getIonMode(this);
const wrapperClass = {
'toast-wrapper': true,
'ion-wrapper': true,
[`toast-${this.position}`]: true
};

View File

@@ -0,0 +1,34 @@
module.exports = {
env: {
browser: true,
node: true,
es6: true,
},
parserOptions: {
project: './tsconfig.json',
ecmaFeatures: {
jsx: true,
},
},
plugins: ['@ionic'],
extends: ['plugin:@ionic/strict'],
rules: {
"@typescript-eslint/no-explicit-any": 0,
"no-negated-condition": 0,
"no-conditional-assignment": 0,
"no-non-null-assertion": 0,
"no-unnecessary-type-assertion": 0,
"no-import-side-effect": 0,
"trailing-comma": 0,
"no-null-keyword": 0,
"no-console": 0,
"no-floating-promises": 0,
"jsx-key": 0,
"jsx-self-close": 0,
"jsx-no-bind": 0,
"jsx-no-lambda": 0,
"jsx-no-multiline-js": 0,
"jsx-wrap-multiline": 0
}
};

1
packages/motion/.npmrc Normal file
View File

@@ -0,0 +1 @@
package-lock=false

View File

@@ -0,0 +1,46 @@
{
"name": "@ionic/motion",
"version": "0.0.1",
"description": "Motion animations for Ionic Framework",
"scripts": {
"clean": "rm -rf dist",
"build": "npm run clean && tsc -p . && cp package.json ./dist/package.json",
"lint": "eslint src/**/*",
"lint.fix": "npm run lint -- --fix"
},
"repository": {
"type": "git",
"url": "git+https://github.com/ionic-team/ionic.git"
},
"keywords": [
"ionic",
"framework",
"react",
"angular",
"mobile",
"app",
"hybrid",
"webapp",
"cordova",
"capacitor",
"progressive",
"web",
"app",
"pwa"
],
"author": "Ionic Team",
"license": "MIT",
"bugs": {
"url": "https://github.com/ionic-team/ionic/issues"
},
"homepage": "https://ionicframework.com/",
"devDependencies": {
"@ionic/eslint-plugin": "0.0.1",
"@typescript-eslint/parser": "^2.33.0",
"eslint": "^7.0.0",
"typescript": "^3.9.2"
},
"dependencies": {
"@ionic/core": "^5.1.1"
}
}

View File

@@ -0,0 +1 @@
# @ionic/motion

View File

@@ -0,0 +1,175 @@
import { Animation, AnimationBuilder, createAnimation } from '@ionic/core';
import { AnimationOptions, Axis } from '../';
const createXAxisTransition = (enteringEl: HTMLElement, leavingEl: HTMLElement, backDirection: boolean): Animation[] => {
const enteringTransition = createAnimation().addElement(enteringEl);
const leavingTransition = createAnimation().addElement(leavingEl);
if (backDirection) {
enteringTransition
.keyframes([
{ offset: 0, opacity: 0, transform: 'translate3d(-30px, 0, 0)' },
{ offset: 0.3, opacity: 0 },
{ offset: 1, opacity: 1, transform: 'translate3d(0, 0, 0)' },
]);
leavingTransition
.keyframes([
{ offset: 0, opacity: 1, transform: 'translate3d(0, 0, 0)' },
{ offset: 0.3, opacity: 0 },
{ offset: 1, opacity: 0, transform: 'translate3d(30px, 0, 0)' },
]);
} else {
enteringTransition
.keyframes([
{ offset: 0, opacity: 0, transform: 'translate3d(30px, 0, 0)' },
{ offset: 0.3, opacity: 0 },
{ offset: 1, opacity: 1, transform: 'translate3d(0, 0, 0)' },
]);
leavingTransition
.keyframes([
{ offset: 0, opacity: 1, transform: 'translate3d(0, 0, 0)' },
{ offset: 0.3, opacity: 0 },
{ offset: 1, opacity: 0, transform: 'translate3d(-30px, 0, 0)' },
]);
}
return [enteringTransition, leavingTransition];
};
const createYAxisTransition = (enteringEl: HTMLElement, leavingEl: HTMLElement, backDirection: boolean): Animation[] => {
const enteringTransition = createAnimation().addElement(enteringEl);
const leavingTransition = createAnimation().addElement(leavingEl);
if (backDirection) {
enteringTransition
.keyframes([
{ offset: 0, opacity: 0, transform: 'translate3d(0, -30px, 0)' },
{ offset: 0.3, opacity: 0 },
{ offset: 1, opacity: 1, transform: 'translate3d(0, 0, 0)' },
]);
leavingTransition
.keyframes([
{ offset: 0, opacity: 1, transform: 'translate3d(0, 0, 0)' },
{ offset: 0.3, opacity: 0 },
{ offset: 1, opacity: 0, transform: 'translate3d(0, 30px, 0)' },
]);
} else {
enteringTransition
.keyframes([
{ offset: 0, opacity: 0, transform: 'translate3d(0, 30px, 0)' },
{ offset: 0.3, opacity: 0 },
{ offset: 1, opacity: 1, transform: 'translate3d(0, 0, 0)' },
]);
leavingTransition
.keyframes([
{ offset: 0, opacity: 1, transform: 'translate3d(0, 0, 0)' },
{ offset: 0.3, opacity: 0 },
{ offset: 1, opacity: 0, transform: 'translate3d(0, -30px, 0)' },
]);
}
return [enteringTransition, leavingTransition];
};
const createFadeZAxisTransition = (enteringEl: HTMLElement, leavingEl: HTMLElement, backDirection: boolean): Animation[] => {
const enteringTransition = createAnimation().addElement(enteringEl);
const leavingTransition = createAnimation().addElement(leavingEl);
if (backDirection) {
enteringTransition
.keyframes([
{ offset: 0, opacity: 0, transform: 'scale3d(1.1, 1.1, 1)' },
{ offset: 1, opacity: 1, transform: 'scale3d(1, 1, 1)' },
]);
leavingTransition
.keyframes([
{ offset: 0, opacity: 1, transform: 'scale3d(1, 1, 1)' },
{ offset: 0.2, opacity: 1 },
{ offset: 0.4, opacity: 0 },
{ offset: 1, opacity: 0, transform: 'scale3d(0.8, 0.8, 1)' },
]);
} else {
enteringTransition
.keyframes([
{ offset: 0, opacity: 0, transform: 'scale3d(0.8, 0.8, 1)' },
{ offset: 0.2, opacity: 0 },
{ offset: 0.4, opacity: 1 },
{ offset: 1, opacity: 1, transform: 'scale3d(1, 1, 1)' },
]);
leavingTransition
.keyframes([
{ offset: 0, transform: 'scale3d(1, 1, 1)' },
{ offset: 1, transform: 'scale3d(1.1, 1.1, 1)' },
]);
}
return [enteringTransition, leavingTransition];
};
const createNormalZAxisTransition = (enteringEl: HTMLElement, leavingEl: HTMLElement, backDirection: boolean): Animation[] => {
const enteringTransition = createAnimation().addElement(enteringEl);
const leavingTransition = createAnimation().addElement(leavingEl);
if (backDirection) {
enteringTransition
.keyframes([
{ offset: 0, opacity: 0, transform: 'scale3d(1.1, 1.1, 1)' },
{ offset: 0.3, opacity: 0 },
{ offset: 1, opacity: 1, transform: 'scale3d(1, 1, 1)' },
]);
leavingTransition
.keyframes([
{ offset: 0, opacity: 1, transform: 'scale3d(1, 1, 1)' },
{ offset: 0.3, opacity: 0 },
{ offset: 1, opacity: 0, transform: 'scale3d(0.8, 0.8, 1)' },
]);
} else {
enteringTransition
.keyframes([
{ offset: 0, opacity: 0, transform: 'scale3d(0.8, 0.8, 1)' },
{ offset: 0.3, opacity: 0 },
{ offset: 1, opacity: 1, transform: 'scale3d(1, 1, 1)' },
]);
leavingTransition
.keyframes([
{ offset: 0, opacity: 1, transform: 'scale3d(1, 1, 1)' },
{ offset: 0.3, opacity: 0 },
{ offset: 1, opacity: 0, transform: 'scale3d(1.1, 1.1, 1)' },
]);
}
return [enteringTransition, leavingTransition];
};
const createZAxisTransition = (enteringEl: HTMLElement, leavingEl: HTMLElement, fade: boolean, backDirection: boolean): Animation[] => fade ? createFadeZAxisTransition(enteringEl, leavingEl, backDirection) : createNormalZAxisTransition(enteringEl, leavingEl, backDirection);
export const sharedAxisTransition = (axis: Axis = 'z', fade: boolean = false, animationOpts: AnimationOptions = {}): AnimationBuilder => {
const { duration, easing } = animationOpts;
return (_: HTMLElement, opts: any): Animation => {
const backDirection = opts.direction === 'back';
const { enteringEl, leavingEl } = opts;
const baseTransition = createAnimation()
.duration(duration !== undefined ? duration : 300)
.easing(easing || 'ease');
switch (axis) {
case 'x':
baseTransition.addAnimation(createXAxisTransition(enteringEl, leavingEl, backDirection));
break;
case 'y':
baseTransition.addAnimation(createYAxisTransition(enteringEl, leavingEl, backDirection));
break;
default:
baseTransition.addAnimation(createZAxisTransition(enteringEl, leavingEl, fade, backDirection));
break;
}
return baseTransition;
};
};

View File

@@ -0,0 +1,63 @@
import { AnimationOptions } from '../';
import { Animation, AnimationBuilder, createAnimation } from '@ionic/core';
const createBase = (animationOpts: AnimationOptions, enteringEl: HTMLElement, leavingEl: HTMLElement): { [key: string]: Animation; } => {
const { duration, easing } = animationOpts;
const rootAnimation = createAnimation()
.duration(duration !== undefined ? duration : 300)
.easing(easing || 'ease');
const leavingAnimation = createAnimation().addElement(leavingEl);
const enteringAnimation = createAnimation().addElement(enteringEl);
rootAnimation.addAnimation([leavingAnimation, enteringAnimation]);
return { rootAnimation, enteringAnimation, leavingAnimation };
};
const enter = (animationOpts: AnimationOptions, enteringEl: HTMLElement, leavingEl: HTMLElement): Animation => {
const { rootAnimation, enteringAnimation, leavingAnimation } = createBase(animationOpts, enteringEl, leavingEl);
leavingAnimation.keyframes([
{ offset: 0, opacity: 1 },
{ offset: 0.3, opacity: 0 },
{ offset: 1, opacity: 0 },
]);
enteringAnimation.keyframes([
{ offset: 0, opacity: 0, transform: 'scale(0.92)' },
{ offset: 0.3, opacity: 0, transform: 'scale(0.92)' },
{ offset: 1, opacity: 1, transform: 'scale(1)' },
]);
return rootAnimation;
};
const leave = (animationOpts: AnimationOptions, enteringEl: HTMLElement, leavingEl: HTMLElement): Animation => {
const { rootAnimation, enteringAnimation, leavingAnimation } = createBase(animationOpts, enteringEl, leavingEl);
leavingAnimation.keyframes([
{ offset: 0, opacity: 1, transform: 'scale(1)' },
{ offset: 0.7, opacity: 0, transform: 'scale(0.92)' },
{ offset: 1, opacity: 0, transform: 'scale(0.92)' },
]);
enteringAnimation.keyframes([
{ offset: 0, opacity: 0 },
{ offset: 0.7, opacity: 0 },
{ offset: 1, opacity: 1 },
]);
return rootAnimation;
};
export const fadeThroughMotionEnter = (presentingEl: HTMLElement, animationOpts: AnimationOptions = {}): AnimationBuilder => (baseEl: HTMLElement): Animation => enter(animationOpts, baseEl, presentingEl);
export const fadeThroughMotionLeave = (presentingEl: HTMLElement, animationOpts: AnimationOptions = {}): AnimationBuilder => (baseEl: HTMLElement): Animation => leave(animationOpts, presentingEl, baseEl);
export const fadeThroughMotion = (animationOpts: AnimationOptions = {}): AnimationBuilder => (_: HTMLElement, opts: any): Animation => {
const backDirection = opts.direction === 'back';
const { enteringEl, leavingEl } = opts;
return backDirection ? leave(animationOpts, enteringEl, leavingEl) : enter(animationOpts, enteringEl, leavingEl);
};

View File

@@ -0,0 +1,72 @@
import { AnimationOptions } from '../';
import { getElementRoot } from '../utils';
import { Animation, AnimationBuilder, createAnimation } from '@ionic/core';
const createBase = (animationOpts: AnimationOptions, enteringEl: HTMLElement): { [key: string]: Animation; } => {
const { duration, easing } = animationOpts;
const rootAnimation = createAnimation()
.duration(duration !== undefined ? duration : 150)
.easing(easing || 'ease');
const elementAnimation = createAnimation().addElement(enteringEl);
rootAnimation.addAnimation([elementAnimation]);
return { rootAnimation, elementAnimation };
};
const enter = (animationOpts: AnimationOptions, baseEl: HTMLElement): Animation => {
const root = getElementRoot(baseEl);
const wrapper = (root.querySelector('.ion-wrapper') || baseEl) as HTMLElement;
const backdrop = root.querySelector('ion-backdrop');
const { rootAnimation, elementAnimation } = createBase(animationOpts, wrapper);
elementAnimation.keyframes([
{ offset: 0, opacity: 0, transform: 'scale(0.8)' },
{ offset: 0.3, opacity: 1 },
{ offset: 1, opacity: 1, transform: 'scale(1)' },
]);
if (backdrop) {
const backdropAnimation = createAnimation()
.addElement(backdrop)
.fromTo('opacity', '0', 'var(--backdrop-opacity, 1)');
rootAnimation.addAnimation(backdropAnimation);
}
return rootAnimation;
};
const leave = (animationOpts: AnimationOptions, baseEl: HTMLElement): Animation => {
const root = getElementRoot(baseEl);
const wrapper = (root.querySelector('.ion-wrapper') || baseEl) as HTMLElement;
const backdrop = root.querySelector('ion-backdrop');
const { rootAnimation, elementAnimation } = createBase(animationOpts, wrapper);
elementAnimation.keyframes([
{ offset: 0, opacity: 1 },
{ offset: 1, opacity: 0 },
]);
if (backdrop) {
const backdropAnimation = createAnimation()
.addElement(backdrop)
.fromTo('opacity', 'var(--backdrop-opacity, 1)', '0');
rootAnimation.addAnimation(backdropAnimation);
}
return rootAnimation;
};
export const fadeMotionEnter = (animationOpts: AnimationOptions = {}): AnimationBuilder => (baseEl: HTMLElement): Animation => enter(animationOpts, baseEl);
export const fadeMotionLeave = (animationOpts: AnimationOptions = {}): AnimationBuilder => (baseEl: HTMLElement): Animation => leave(animationOpts, baseEl);
export const fadeMotion = (animationOpts: AnimationOptions = {}): AnimationBuilder => (_: HTMLElement, opts: any): Animation => {
const backDirection = opts.direction === 'back';
const { enteringEl, leavingEl } = opts;
return backDirection ? leave(animationOpts, leavingEl) : enter(animationOpts, enteringEl);
};

View File

@@ -0,0 +1,10 @@
export { sharedAxisTransition } from './animations/axis.motion';
export { fadeMotion, fadeMotionEnter, fadeMotionLeave } from './animations/fade.motion';
export { fadeThroughMotion, fadeThroughMotionEnter, fadeThroughMotionLeave } from './animations/fade-through.motion';
export interface AnimationOptions {
duration?: number;
easing?: string;
}
export type Axis = 'x' | 'y' | 'z';

View File

@@ -0,0 +1 @@
export const getElementRoot = (el: HTMLElement, fallback: HTMLElement = el) => el.shadowRoot || fallback;

View File

@@ -0,0 +1,35 @@
{
"compilerOptions": {
"strict": true,
"alwaysStrict": true,
"allowSyntheticDefaultImports": true,
"allowUnreachableCode": false,
"declaration": true,
"experimentalDecorators": true,
"forceConsistentCasingInFileNames": true,
"assumeChangesOnlyAffectDirectDependencies": true,
"jsx": "react",
"jsxFactory": "h",
"lib": [
"dom",
"es2017"
],
"module": "esnext",
"moduleResolution": "node",
"noImplicitAny": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"outDir": "dist",
"pretty": true,
"removeComments": false,
"target": "es2017"
},
"include": [
"src",
],
"exclude": [
"node_modules",
"**/test"
]
}