Ionic version:
[x] 4.x
Describe the Feature Request
I want to use Android transitions on iOS because they are faster and look better with my current design. Like you can see in the GIF below, the iOS transitions doesn't work quite well with my app. The header/toolbar is visible quite to long.
Describe Preferred Solution
A simple solution to use Android transitions on iOS without creating a custom Animation with AnimationBuilder. Would be nice if you could add a configuration like in Ionic 3 to easy change the transitions of the platform: https://ionicframework.com/docs/api/config/Config/
Describe Alternatives
Currently I'm using the ModalController to fix this issue. The problem is that the page can't be lazy loaded by now.
Related Code
Additional Context
I'm using CLI in Version 4.6.0 and Framework in Version RC-0.
ionic3
There are similar problems
Hi @mariusbolik,
you can try my custom animation for your app
in app.module.ts add:
import { Animation, NavOptions } from '@ionic/core';
@NgModule({
declarations: [
AppComponent,
],
entryComponents: [],
imports: [
...
IonicModule.forRoot({
navAnimation: myTransitionAnimation,
}),
],
providers: [
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
],
bootstrap: [AppComponent]
})
export function myTransitionAnimation(AnimationC: Animation, _: HTMLElement, opts: TransitionOptions): Promise<Animation> {
const TRANSLATE_DIRECTION = 'translateX';
const OFF_BOTTOM = '100%';
const CENTER = '0px';
const enteringEl = opts.enteringEl;
const leavingEl = opts.leavingEl;
const ionPageElement = getIonPageElement(enteringEl);
const rootTransition = new AnimationC();
rootTransition
.addElement(ionPageElement)
.beforeRemoveClass('ion-page-invisible');
const backDirection = (opts.direction === 'back');
// animate the component itself
if (backDirection) {
rootTransition
.duration(opts.duration || 350)
.easing('cubic-bezier(0.3,0,0.66,1)');
} else {
rootTransition
.duration(opts.duration || 350)
.easing('cubic-bezier(0.3,0,0.66,1)')
.fromTo(TRANSLATE_DIRECTION, OFF_BOTTOM, CENTER, true)
.fromTo('opacity', 1, 1, true);
}
// Animate toolbar if it's there
const enteringToolbarEle = ionPageElement.querySelector('ion-toolbar');
if (enteringToolbarEle) {
const enteringToolBar = new AnimationC();
enteringToolBar.addElement(enteringToolbarEle);
rootTransition.add(enteringToolBar);
}
// setup leaving view
if (leavingEl && backDirection) {
rootTransition
.duration(opts.duration || 350)
.easing('cubic-bezier(0.3,0,0.66,1)');
const leavingPage = new AnimationC();
leavingPage
.addElement(getIonPageElement(leavingEl))
.fromTo(TRANSLATE_DIRECTION, CENTER, OFF_BOTTOM)
.fromTo('opacity', 1, 1);
rootTransition.add(leavingPage);
}
return Promise.resolve(rootTransition);
}
export interface TransitionOptions extends NavOptions {
animationCtrl: HTMLIonAnimationControllerElement;
progressCallback?: ((ani: Animation | undefined) => void);
window: Window;
baseEl: any;
enteringEl: HTMLElement;
leavingEl: HTMLElement | undefined;
}
function getIonPageElement(element: HTMLElement) {
if (element.classList.contains('ion-page')) {
return element;
}
const ionPage = element.querySelector(':scope > .ion-page, :scope > ion-nav, :scope > ion-tabs');
if (ionPage) {
return ionPage;
}
return element;
}
Thank you so much @duyetnk,
this really helps a lot!
thank you @duyetnk, you solved my problem too
glad to help you guy
Great solution. Is there a way to do this only for platform ios?
update, found out a way to do this only for iOS:
let ionic = [
IonicModule.forRoot()
];
const platform = new Platform();
if (platform.is('ios')) {
ionic = [
IonicModule.forRoot({
navAnimation: myTransitionAnimation,
})
]
}
and in module imports:
imports: [
...ionic,
]
@duyetnk hi! I am dealing with animation issues with iOS so I was looking in to checking your custom animation. It appears that HTMLIonAnimationControllerElement
interface for animationCtrl
no longer exists in 4.0.0-rc.2
? I copied your code as is, what am I missing? Thanks!
hi uncvrd,
you can copy transitionoptions from here
https://github.com/ionic-team/ionic/blob/master/core/src/utils/transition/index.ts
or import whole file, i didnt test it yet
UPDATE: the ionic team has changed source code,
you guys can go there to update your transition
https://github.com/ionic-team/ionic/blob/master/core/src/utils/transition/
Hi @mariusbolik,
you can try my custom animation for your app
in app.module.ts add:import { Animation, NavOptions } from '@ionic/core'; @NgModule({ declarations: [ AppComponent, ], entryComponents: [], imports: [ ... IonicModule.forRoot({ navAnimation: myTransitionAnimation, }), ], providers: [ { provide: RouteReuseStrategy, useClass: IonicRouteStrategy } ], bootstrap: [AppComponent] }) export function myTransitionAnimation(AnimationC: Animation, _: HTMLElement, opts: TransitionOptions): Promise<Animation> { const TRANSLATE_DIRECTION = 'translateX'; const OFF_BOTTOM = '100%'; const CENTER = '0px'; const enteringEl = opts.enteringEl; const leavingEl = opts.leavingEl; const ionPageElement = getIonPageElement(enteringEl); const rootTransition = new AnimationC(); rootTransition .addElement(ionPageElement) .beforeRemoveClass('ion-page-invisible'); const backDirection = (opts.direction === 'back'); // animate the component itself if (backDirection) { rootTransition .duration(opts.duration || 350) .easing('cubic-bezier(0.3,0,0.66,1)'); } else { rootTransition .duration(opts.duration || 350) .easing('cubic-bezier(0.3,0,0.66,1)') .fromTo(TRANSLATE_DIRECTION, OFF_BOTTOM, CENTER, true) .fromTo('opacity', 1, 1, true); } // Animate toolbar if it's there const enteringToolbarEle = ionPageElement.querySelector('ion-toolbar'); if (enteringToolbarEle) { const enteringToolBar = new AnimationC(); enteringToolBar.addElement(enteringToolbarEle); rootTransition.add(enteringToolBar); } // setup leaving view if (leavingEl && backDirection) { rootTransition .duration(opts.duration || 350) .easing('cubic-bezier(0.3,0,0.66,1)'); const leavingPage = new AnimationC(); leavingPage .addElement(getIonPageElement(leavingEl)) .fromTo(TRANSLATE_DIRECTION, CENTER, OFF_BOTTOM) .fromTo('opacity', 1, 1); rootTransition.add(leavingPage); } return Promise.resolve(rootTransition); } export interface TransitionOptions extends NavOptions { animationCtrl: HTMLIonAnimationControllerElement; progressCallback?: ((ani: Animation | undefined) => void); window: Window; baseEl: any; enteringEl: HTMLElement; leavingEl: HTMLElement | undefined; } function getIonPageElement(element: HTMLElement) { if (element.classList.contains('ion-page')) { return element; } const ionPage = element.querySelector(':scope > .ion-page, :scope > ion-nav, :scope > ion-tabs'); if (ionPage) { return ionPage; } return element; }
where is HTMLIonAnimationControllerElement ?
just remove the folowing line:
animationCtrl: HTMLIonAnimationControllerElement;
update, found out a way to do this only for iOS:
let ionic = [ IonicModule.forRoot() ]; const platform = new Platform(); if (platform.is('ios')) { ionic = [ IonicModule.forRoot({ navAnimation: myTransitionAnimation, }) ] }
and in module imports:
imports: [ ...ionic, ]
I am facing the same issue. while navigating one screen to other screen in iOS.
but in Android it is working as expected.
@elmartino could you please give more elaborate on your answer. it could help full.
Thanks in Advance.
thanks!!! . a lot of thanks!. it's working. ;)
I think the simplest way to use Android Transitions on iOS is the following:
import { mdTransitionAnimation } from '@ionic/core/dist/collection/utils/transition/md.transition';
...
IonicModule.forRoot({
navAnimation: mdTransitionAnimation
}),
This also works for Ionic 4.8+
Hi @mariusbolik do you know how to do this on specific page only? Not the entire page as you did.
Hi @mirzailhami, did you find a way to change the page transition animation for a specific page?
And also, I'm not able to build my app with --prod when I try to use the solution proposed by @mariusbolik
I get the following error
ERROR in app/app.module.ts(113,21): Error during template compile of 'AppModule'
Function expressions are not supported in decorators in 'mdTransitionAnimation'
'mdTransitionAnimation' contains the error at ../@ionic/core/dist/collection/utils/transition/md.transition.js.ts(1,38)
Consider changing the function expression into an exported function.
Is there a workaround that makes it possible? Thank you guys.
@mateusduraes, you need to wrap the exported function expression into your own function:
import { mdTransitionAnimation } from '@ionic/core/dist/collection/utils/transition/md.transition';
import { Animation } from '@ionic/core';
export function transitionAnimation(foo: Animation, el: HTMLElement, opts: any): Promise<Animation> {
return Promise.resolve(mdTransitionAnimation(el, opts));
}
...and use that one in the decorator:
IonicModule.forRoot({
navAnimation: transitionAnimation
}),
Hi @damirarh, thank you, it works.
I just made a small fix in your code because it was giving me some errors.
return Promise.resolve(mdTransitionAnimation(foo, el, opts))
Is this feature considered to be available in the next releases? In Ionic 3 we
was able to change the page animation based on the active platform (android | ios)
@mateusduraes, I managed to use a different animation based on the platform using the following code:
import { Animation, isPlatform } from '@ionic/core';
export function transitionAnimation(AnimationC: Animation, el: HTMLElement, opts: TransitionOptions): Promise<Animation> {
if (isPlatform('ios')) {
// invoke iOS animation
} else {
// invoke Android animation
}
}
Still, it would be great to have a built-in way to do this in Ionic.
Hi @mariusbolik,
you can try my custom animation for your app
in app.module.ts add:import { Animation, NavOptions } from '@ionic/core'; @NgModule({ declarations: [ AppComponent, ], entryComponents: [], imports: [ ... IonicModule.forRoot({ navAnimation: myTransitionAnimation, }), ], providers: [ { provide: RouteReuseStrategy, useClass: IonicRouteStrategy } ], bootstrap: [AppComponent] }) export function myTransitionAnimation(AnimationC: Animation, _: HTMLElement, opts: TransitionOptions): Promise<Animation> { const TRANSLATE_DIRECTION = 'translateX'; const OFF_BOTTOM = '100%'; const CENTER = '0px'; const enteringEl = opts.enteringEl; const leavingEl = opts.leavingEl; const ionPageElement = getIonPageElement(enteringEl); const rootTransition = new AnimationC(); rootTransition .addElement(ionPageElement) .beforeRemoveClass('ion-page-invisible'); const backDirection = (opts.direction === 'back'); // animate the component itself if (backDirection) { rootTransition .duration(opts.duration || 350) .easing('cubic-bezier(0.3,0,0.66,1)'); } else { rootTransition .duration(opts.duration || 350) .easing('cubic-bezier(0.3,0,0.66,1)') .fromTo(TRANSLATE_DIRECTION, OFF_BOTTOM, CENTER, true) .fromTo('opacity', 1, 1, true); } // Animate toolbar if it's there const enteringToolbarEle = ionPageElement.querySelector('ion-toolbar'); if (enteringToolbarEle) { const enteringToolBar = new AnimationC(); enteringToolBar.addElement(enteringToolbarEle); rootTransition.add(enteringToolBar); } // setup leaving view if (leavingEl && backDirection) { rootTransition .duration(opts.duration || 350) .easing('cubic-bezier(0.3,0,0.66,1)'); const leavingPage = new AnimationC(); leavingPage .addElement(getIonPageElement(leavingEl)) .fromTo(TRANSLATE_DIRECTION, CENTER, OFF_BOTTOM) .fromTo('opacity', 1, 1); rootTransition.add(leavingPage); } return Promise.resolve(rootTransition); } export interface TransitionOptions extends NavOptions { animationCtrl: HTMLIonAnimationControllerElement; progressCallback?: ((ani: Animation | undefined) => void); window: Window; baseEl: any; enteringEl: HTMLElement; leavingEl: HTMLElement | undefined; } function getIonPageElement(element: HTMLElement) { if (element.classList.contains('ion-page')) { return element; } const ionPage = element.querySelector(':scope > .ion-page, :scope > ion-nav, :scope > ion-tabs'); if (ionPage) { return ionPage; } return element; }
Thanks, i am using this code, but i have audio tag to play audios on my pages, Animation is causing it width to reduce. How can i set it up so that it does not reduce audio's width?
Hi @mariusbolik,
you can try my custom animation for your app
in app.module.ts add:import { Animation, NavOptions } from '@ionic/core'; @NgModule({ declarations: [ AppComponent, ], entryComponents: [], imports: [ ... IonicModule.forRoot({ navAnimation: myTransitionAnimation, }), ], providers: [ { provide: RouteReuseStrategy, useClass: IonicRouteStrategy } ], bootstrap: [AppComponent] }) export function myTransitionAnimation(AnimationC: Animation, _: HTMLElement, opts: TransitionOptions): Promise<Animation> { const TRANSLATE_DIRECTION = 'translateX'; const OFF_BOTTOM = '100%'; const CENTER = '0px'; const enteringEl = opts.enteringEl; const leavingEl = opts.leavingEl; const ionPageElement = getIonPageElement(enteringEl); const rootTransition = new AnimationC(); rootTransition .addElement(ionPageElement) .beforeRemoveClass('ion-page-invisible'); const backDirection = (opts.direction === 'back'); // animate the component itself if (backDirection) { rootTransition .duration(opts.duration || 350) .easing('cubic-bezier(0.3,0,0.66,1)'); } else { rootTransition .duration(opts.duration || 350) .easing('cubic-bezier(0.3,0,0.66,1)') .fromTo(TRANSLATE_DIRECTION, OFF_BOTTOM, CENTER, true) .fromTo('opacity', 1, 1, true); } // Animate toolbar if it's there const enteringToolbarEle = ionPageElement.querySelector('ion-toolbar'); if (enteringToolbarEle) { const enteringToolBar = new AnimationC(); enteringToolBar.addElement(enteringToolbarEle); rootTransition.add(enteringToolBar); } // setup leaving view if (leavingEl && backDirection) { rootTransition .duration(opts.duration || 350) .easing('cubic-bezier(0.3,0,0.66,1)'); const leavingPage = new AnimationC(); leavingPage .addElement(getIonPageElement(leavingEl)) .fromTo(TRANSLATE_DIRECTION, CENTER, OFF_BOTTOM) .fromTo('opacity', 1, 1); rootTransition.add(leavingPage); } return Promise.resolve(rootTransition); } export interface TransitionOptions extends NavOptions { animationCtrl: HTMLIonAnimationControllerElement; progressCallback?: ((ani: Animation | undefined) => void); window: Window; baseEl: any; enteringEl: HTMLElement; leavingEl: HTMLElement | undefined; } function getIonPageElement(element: HTMLElement) { if (element.classList.contains('ion-page')) { return element; } const ionPage = element.querySelector(':scope > .ion-page, :scope > ion-nav, :scope > ion-tabs'); if (ionPage) { return ionPage; } return element; }
Updated to work with Ionic 5 Beta 2:
import { Animation, NavOptions, createAnimation } from '@ionic/core';
export const myTransitionAnimation = (_: HTMLElement, opts: TransitionOptions): Animation => {
const TRANSLATE_DIRECTION = 'translateX';
const OFF_BOTTOM = '100%';
const CENTER = '0px';
const enteringEl = opts.enteringEl;
const leavingEl = opts.leavingEl;
// tslint:disable-next-line: no-use-before-declare
const ionPageElement = getIonPageElement(enteringEl);
const rootTransition = createAnimation();
rootTransition
.addElement(ionPageElement)
.fill('both')
.beforeRemoveClass('ion-page-invisible');
const backDirection = (opts.direction === 'back');
// animate the component itself
if (backDirection) {
rootTransition
.duration(opts.duration || 100)
.easing('cubic-bezier(0.3,0,0.66,1)');
} else {
rootTransition
.duration(opts.duration || 100)
.easing('cubic-bezier(0.3,0,0.66,1)')
.fromTo('transform', `translateX(${OFF_BOTTOM})`, `translateX(${CENTER})`)
.fromTo('opacity', 1, 1);
}
// Animate toolbar if it's there
const enteringToolbarEle = ionPageElement.querySelector('ion-toolbar');
if (enteringToolbarEle) {
const enteringToolBar = createAnimation();
enteringToolBar.addElement(enteringToolbarEle);
rootTransition.addAnimation(enteringToolBar);
}
// setup leaving view
if (leavingEl && backDirection) {
// leaving content
rootTransition
.duration(opts.duration || 100)
.easing('cubic-bezier(0.3,0,0.66,1)');
const leavingPage = createAnimation();
leavingPage
// tslint:disable-next-line: no-use-before-declare
.addElement(getIonPageElement(leavingEl))
.afterStyles({ 'display': 'none' })
.fromTo('transform', `translateX(${CENTER})`, `translateX(${OFF_BOTTOM})`)
.fromTo('opacity', 1, 1);
rootTransition.addAnimation(leavingPage);
}
return rootTransition;
};
export interface TransitionOptions extends NavOptions {
progressCallback?: ((ani: Animation | undefined) => void);
baseEl: any;
enteringEl: HTMLElement;
leavingEl: HTMLElement | undefined;
}
export const getIonPageElement = (element: HTMLElement) => {
if (element.classList.contains('ion-page')) {
return element;
}
const ionPage = element.querySelector(':scope > .ion-page, :scope > ion-nav, :scope > ion-tabs');
if (ionPage) {
return ionPage;
}
// idk, return the original element so at least something animates and we don't have a null pointer
return element;
};
@mateusduraes, you need to wrap the exported function expression into your own function:
import { mdTransitionAnimation } from '@ionic/core/dist/collection/utils/transition/md.transition'; import { Animation } from '@ionic/core'; export function transitionAnimation(foo: Animation, el: HTMLElement, opts: any): Promise<Animation> { return Promise.resolve(mdTransitionAnimation(el, opts)); }
...and use that one in the decorator:
IonicModule.forRoot({ navAnimation: transitionAnimation }),
I get error as
Module not found: Error: Can't resolve '@stencil/core'
import { mdTransitionAnimation } from '@ionic/core';
IonicModule.forRoot({
mode: 'ios',
navAnimation: mdTransitionAnimation
})
Use this for Ionic 5:
import { mdTransitionAnimation } from '@ionic/core';
IonicModule.forRoot({ mode: 'ios', navAnimation: mdTransitionAnimation })
thank you, now it says:
ERROR in src/app/app.module.ts(42,21): Error during template compile of 'AppModule'
Only initialized variables and constants can be referenced in decorators because the value of this variable is needed by the template compiler in 'mdTransitionAnimation'
'mdTransitionAnimation' references 'mdTransitionAnimation'
'mdTransitionAnimation' references 'mdTransitionAnimation'
'mdTransitionAnimation' is not initialized at @ionic/core/dist/types/utils/transition/md.transition.ts(3,22).
I can't make it works =(
Thanks for the issue. I am going to close this as this is now possible Ionic Framework.
Ionic Angular example:
import { mdTransitionAnimation } from '@ionic/angular';
IonicModule.forRoot({
navAnimation: mdTransitionAnimation
});
Note: This requires Ionic Framework v5.1.0 or newer.
Thanks for the issue! This issue is being locked to prevent comments that are not relevant to the original issue. If this is still an issue with the latest version of Ionic, please create a new issue and ensure the template is fully filled out.
Most helpful comment
Hi @mariusbolik,
you can try my custom animation for your app
in app.module.ts add: