Reputation: 19453
Ionic modal comes with the standard animation of slide-in-up
. Is it possible that we can change the animation to fade-in
?
Upvotes: 5
Views: 13195
Reputation: 11
Few days ago I got the same issue on Angular 2. I solved it like that:
I have two different components:
Here, you need to import "ModalController" and then you need to pass its own istance to ModalProfileComponent constructor.
import { ModalController } from '@ionic/angular';
export class ModalProfileComponent {
constructor( public modal: ModalController ) { }
}
Also, I used this class to structurally and graphically edit the modal (HTML and CSS).
First of all, import AnimationController, ModalController and the modal class you created at the first step:
import { AnimationController } from '@ionic/angular';
import { ModalController } from '@ionic/angular';
import { ModalProfileComponent } from '../../home/components/modal-profile.component';
Then, add as constructor argument the following instances:
constructor(private modal: ModalController, private animationCtrl: AnimationController) {}
Now, declare a method you will call by clicking on the HTML element you want. I will use onOptionsClick().
In this method, you can now use the ion-modal function enterAnimation and leaveAnimation to customize the modal behaviour when it opens and closes.
You can follow the ionic documentation, section "Modals" at this link.
Following, the solution I improved:
async onOptionsClick() {
const enterAnimation = (baseEl: HTMLElement) => {
const root = baseEl.shadowRoot;
const backdropAnimation = this.animationCtrl.create()
.addElement(root.querySelector('ion-backdrop')!)
.fromTo('opacity', '0.01', 'var(--backdrop-opacity)');
const wrapperAnimation = this.animationCtrl.create()
.addElement(root.querySelector('.modal-wrapper')!)
.fromTo('transform', 'translateY(-100%)', 'translateY(0)');
return this.animationCtrl.create()
.addElement(baseEl)
.easing('ease-out')
.duration(500)
.addAnimation([backdropAnimation, wrapperAnimation]);
}
const leaveAnimation = (baseEl: HTMLElement) => {
return enterAnimation(baseEl).direction('reverse');
}
const modal = await this.modal.create({
component: ModalProfileComponent,
enterAnimation,
leaveAnimation
});
modal.present();
}
Hope you find usefull! :)
Upvotes: 1
Reputation: 36
import { AnimationController } from '@ionic/angular';
export const SwipeToCloseDefaults = {
MIN_PRESENTING_SCALE: 0.93,
};
export const enterFromRightAnimation = (baseEl, presentingEl) => {
const backdropAnimation = new AnimationController().create()
.addElement(baseEl.querySelector('ion-backdrop'))
.fromTo('opacity', 0.01, 'var(--backdrop-opacity)')
.beforeStyles({
'pointer-events': 'none'
})
.afterClearStyles(['pointer-events']);
const wrapperAnimation = new AnimationController().create()
.addElement(baseEl.querySelectorAll('.modal-wrapper, .modal-shadow'))
.beforeStyles({ 'opacity': 1 })
.fromTo('transform', 'translateX(100vh)', 'translateX(0vh)');
const baseAnimation = new AnimationController().create()
.addElement(baseEl)
.easing('cubic-bezier(0.32,0.72,0,1)')
.duration(500)
.addAnimation(wrapperAnimation);
if (presentingEl) {
const isMobile = window.innerWidth < 768;
const hasCardModal = (presentingEl.tagName === 'ION-MODAL' && presentingEl.presentingElement !== undefined);
const presentingAnimation = new AnimationController().create()
.beforeStyles({
'transform': 'translateX(0)',
'transform-origin': 'top center',
'overflow': 'hidden'
});
const bodyEl = document.body;
if (isMobile) {
/**
* Fallback for browsers that does not support `max()` (ex: Firefox)
* No need to worry about statusbar padding since engines like Gecko
* are not used as the engine for standlone Cordova/Capacitor apps
*/
const transformOffset = (!CSS.supports('width', 'max(0px, 1px)')) ? '30px' : 'max(30px, var(--ion-safe-area-top))';
const modalTransform = hasCardModal ? '-10px' : transformOffset;
const toPresentingScale = SwipeToCloseDefaults.MIN_PRESENTING_SCALE;
const finalTransform = `translateX(${modalTransform}) scale(${toPresentingScale})`;
presentingAnimation
.afterStyles({
'transform': finalTransform
})
.beforeAddWrite(() => bodyEl.style.setProperty('background-color', 'black'))
.addElement(presentingEl)
.keyframes([
{ offset: 0, filter: 'contrast(1)', transform: 'translateX(0px) scale(1)', borderRadius: '0px' },
{ offset: 1, filter: 'contrast(0.85)', transform: finalTransform, borderRadius: '10px 10px 0 0' }
]);
baseAnimation.addAnimation(presentingAnimation);
}
else {
baseAnimation.addAnimation(backdropAnimation);
if (!hasCardModal) {
wrapperAnimation.fromTo('opacity', '0', '1');
}
else {
const toPresentingScale = (hasCardModal) ? SwipeToCloseDefaults.MIN_PRESENTING_SCALE : 1;
const finalTransform = `translateX(-10px) scale(${toPresentingScale})`;
presentingAnimation
.afterStyles({
'transform': finalTransform
})
.addElement(presentingEl.querySelector('.modal-wrapper'))
.keyframes([
{ offset: 0, filter: 'contrast(1)', transform: 'translateX(0) scale(1)' },
{ offset: 1, filter: 'contrast(0.85)', transform: finalTransform }
]);
const shadowAnimation =new AnimationController().create()
.afterStyles({
'transform': finalTransform
})
.addElement(presentingEl.querySelector('.modal-shadow'))
.keyframes([
{ offset: 0, opacity: '1', transform: 'translateX(0) scale(1)' },
{ offset: 1, opacity: '0', transform: finalTransform }
]);
baseAnimation.addAnimation([presentingAnimation, shadowAnimation]);
}
}
}
else {
baseAnimation.addAnimation(backdropAnimation);
}
return baseAnimation;
};
export const leaveToRightAnimation = (baseEl, presentingEl, duration = 500) => {
const backdropAnimation = new AnimationController().create()
.addElement(baseEl.querySelector('ion-backdrop'))
.fromTo('opacity', 'var(--backdrop-opacity)', 0.0);
const wrapperAnimation = new AnimationController().create()
.addElement(baseEl.querySelectorAll('.modal-wrapper, .modal-shadow'))
.beforeStyles({ 'opacity': 1 })
.fromTo('transform', 'translateX(0vh)', 'translateX(100vh)');
const baseAnimation = new AnimationController().create()
.addElement(baseEl)
.easing('cubic-bezier(0.32,0.72,0,1)')
.duration(duration)
.addAnimation(wrapperAnimation);
if (presentingEl) {
const isMobile = window.innerWidth < 768;
const hasCardModal = (presentingEl.tagName === 'ION-MODAL' && presentingEl.presentingElement !== undefined);
const presentingAnimation = new AnimationController().create()
.beforeClearStyles(['transform'])
.afterClearStyles(['transform'])
.onFinish(currentStep => {
// only reset background color if this is the last card-style modal
if (currentStep !== 1) {
return;
}
presentingEl.style.setProperty('overflow', '');
const numModals = Array.from(bodyEl.querySelectorAll('ion-modal')).filter(m => m.presentingElement !== undefined).length;
if (numModals <= 1) {
bodyEl.style.setProperty('background-color', '');
}
});
const bodyEl = document.body;
if (isMobile) {
const transformOffset = (!CSS.supports('width', 'max(0px, 1px)')) ? '30px' : 'max(30px, var(--ion-safe-area-top))';
const modalTransform = hasCardModal ? '-10px' : transformOffset;
const toPresentingScale = SwipeToCloseDefaults.MIN_PRESENTING_SCALE;
const finalTransform = `translateX(${modalTransform}) scale(${toPresentingScale})`;
presentingAnimation
.addElement(presentingEl)
.keyframes([
{ offset: 0, filter: 'contrast(0.85)', transform: finalTransform, borderRadius: '10px 10px 0 0' },
{ offset: 1, filter: 'contrast(1)', transform: 'translateX(0px) scale(1)', borderRadius: '0px' }
]);
baseAnimation.addAnimation(presentingAnimation);
}
else {
baseAnimation.addAnimation(backdropAnimation);
if (!hasCardModal) {
wrapperAnimation.fromTo('opacity', '1', '0');
}
else {
const toPresentingScale = (hasCardModal) ? SwipeToCloseDefaults.MIN_PRESENTING_SCALE : 1;
const finalTransform = `translateX(-10px) scale(${toPresentingScale})`;
presentingAnimation
.addElement(presentingEl.querySelector('.modal-wrapper'))
.afterStyles({
'transform': 'translate3d(0, 0, 0)'
})
.keyframes([
{ offset: 0, filter: 'contrast(0.85)', transform: finalTransform },
{ offset: 1, filter: 'contrast(1)', transform: 'translateX(0) scale(1)' }
]);
const shadowAnimation = new AnimationController().create()
.addElement(presentingEl.querySelector('.modal-shadow'))
.afterStyles({
'transform': 'translateX(0) scale(1)'
})
.keyframes([
{ offset: 0, opacity: '0', transform: finalTransform },
{ offset: 1, opacity: '1', transform: 'translateX(0) scale(1)' }
]);
baseAnimation.addAnimation([presentingAnimation, shadowAnimation]);
}
}
}
else {
baseAnimation.addAnimation(backdropAnimation);
}
return baseAnimation;
};
Upvotes: 0
Reputation: 3287
You could use the ready animation from this opensource:
Modal / Popover transition class for Ionic v3 here
Upvotes: 0
Reputation: 1868
In order to add custom transitions for Ionic Modal We will be using Ionic Modal Options enterAnimation
and leaveAnimationfrom
ModalOptions interface. For a modal there are transition states: On Enter of modal and and On Leave of modal when we close it. If you look at the Ionic Modal options interface you will find 2 options to add animations for both the states.
export interface ModalOptions {
showBackdrop?: boolean;
enableBackdropDismiss?: boolean;
enterAnimation?: string;
leaveAnimation?: string;
cssClass?: string;
}
We will use these options in modal to specify transition class we create using Animation class from ionic-angular
. So lets see how we can create and custom animations step by step.
Create 2 transition classes for enter and leave:
on-enter-translate.transition.ts
import { Animation, PageTransition } from 'ionic-angular';
export class ModalTranslateEnterTransition extends PageTransition {
public init() {
const ele = this.enteringView.pageRef().nativeElement;
const wrapper = new Animation(this.plt, ele.querySelector('.modal-wrapper'));
wrapper.beforeStyles({ 'transform': 'translateX(100%);', 'opacity': 1 });
wrapper.fromTo('transform', 'translateX(100%)', 'translateX(0)');
wrapper.fromTo('opacity', 1, 1);
this
.element(this.enteringView.pageRef())
.duration(500)
.easing('cubic-bezier(.1, .7, .1, 1)')
.add(wrapper);
}
}
on-leave-translate.transition.ts
import { Animation, PageTransition } from 'ionic-angular';
export class ModalTranslateLeaveTransition extends PageTransition {
public init() {
const ele = this.leavingView.pageRef().nativeElement;
const wrapper = new Animation(this.plt, ele.querySelector('.modal-wrapper'));
const contentWrapper = new Animation(this.plt, ele.querySelector('.wrapper'));
wrapper.beforeStyles({ 'transform': 'translateX(100%)', 'opacity': 1 });
wrapper.fromTo('transform', 'translateX(0)', 'translateX(100%)');
wrapper.fromTo('opacity', 1, 1);
contentWrapper.fromTo('opacity', 1, 0);
this
.element(this.leavingView.pageRef())
.duration(500)
.easing('cubic-bezier(.1, .7, .1, 1)')
.add(contentWrapper)
.add(wrapper);
}
}
Then import those modules in app.module.ts
export class AppModule {
constructor(public config: Config) {
this.setCustomTransitions();
}
private setCustomTransitions() {
this.config.setTransition('modal-translate-up-enter', ModalTranslateEnterTransition);
this.config.setTransition('modal-translate-up-leave', ModalTranslateLeaveTransition);
}
}
And create modal using following options:
var modal = this.modalCtrl.create(AddToCartModalPage, {
productId: this.productId,
skuId: this.skuId,
zipcode: this.zipcode,
sellerProfileId: this.sellerProfileId,
branchId: this.branchId,
changeSeller: this.changeSeller
}, {
showBackdrop: false,
enableBackdropDismiss: false,
cssClass: 'add-to-cart-modal',
enterAnimation: 'modal-translate-up-enter',
leaveAnimation: 'modal-translate-up-leave'
});
Find more information my article here: Blog
Find complete demo repository here: Github
Upvotes: 6
Reputation: 1380
You can add your own animation css, ex:
.slide-in-right {
-webkit-transform: translateX(100%);
transform: translateX(100%); }
.slide-in-right.ng-enter, .slide-in-right > .ng-enter {
-webkit-transition: all cubic-bezier(0.1, 0.7, 0.1, 1) 400ms;
transition: all cubic-bezier(0.1, 0.7, 0.1, 1) 400ms; }
.slide-in-right.ng-enter-active, .slide-in-right > .ng-enter-active {
-webkit-transform: translateX(0);
transform: translateX(0); }
.slide-in-right.ng-leave, .slide-in-right > .ng-leave {
-webkit-transition: all ease-in-out 250ms;
transition: all ease-in-out 250ms; }
and usage with 'slide-in-right'
the same for 'fade-in'
https://forum.ionicframework.com/t/slide-in-right-animation-for-ionicmodal/18882
Upvotes: 3
Reputation: 2171
no ionic does not provide fade-in animation. But you can do that by using animate.css
Upvotes: 0