Customizing Page Transitions in Ionic 5

May 1st 2020 Ionic 4+

Although Ionic supports custom transitions when navigating between pages, I couldn't find much documentation about it. However, by combining information from different sources I managed to create one and fully understand the code involved.

Custom Ionic page transition

The default page transition can be easily replaced through Ionic Config. The custom animation can be specified as a value for the navAnimation field:

@NgModule({
  // ...
  imports: [
    BrowserModule,
    IonicModule.forRoot({ navAnimation: pageTransition }),
    AppRoutingModule
  ],
  // ...
})
export class AppModule {}

This bit is documented but the documentation doesn't explain what should be passed as the value. The best starting points are the default Android and iOS page transitions. The value expected by the navAnimation field is the animation factory function with the following signature:

function pageTransition(_: HTMLElement, opts: TransitionOptions): Animation;

Since Ionic doesn't seem to export the TransitionOptions in the signature above, you'll need to copy it into your application from Ionic source code:

export interface TransitionOptions extends NavOptions {
  progressCallback?: ((ani: Animation | undefined) => void);
  baseEl: any;
  enteringEl: HTMLElement;
  leavingEl: HTMLElement | undefined;
}

The same goes for the getIonPageElement() helper function that's used in both Android and iOS default page transitions:

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;
};

Since the default Android page transition is much simpler from the iOS one, I based my custom page transition on its source code. After some refactoring, I came up with the following well-structured function (inline comments explain what individual parts do) which allows for easy modifications to the transition animation:

export function pageTransition(_: HTMLElement, opts: TransitionOptions) {
  const DURATION = 300;

  // root animation with common setup for the whole transition
  const rootTransition = createAnimation()
    .duration(opts.duration || DURATION)
    .easing('cubic-bezier(0.3,0,0.66,1)');

  // ensure that the entering page is visible from the start of the transition
  const enteringPage = createAnimation()
    .addElement(getIonPageElement(opts.enteringEl))
    .beforeRemoveClass('ion-page-invisible');

  // create animation for the leaving page
  const leavingPage = createAnimation().addElement(
    getIonPageElement(opts.leavingEl)
  );

  // actual customized animation
  if (opts.direction === 'forward') {
    enteringPage.fromTo('transform', 'translateX(100%)', 'translateX(0)');
    leavingPage.fromTo('opacity', '1', '0.25');
  } else {
    leavingPage.fromTo('transform', 'translateX(0)', 'translateX(100%)');
    enteringPage.fromTo('opacity', '0.25', '1');
  }

  // include animations for both pages into the root animation
  rootTransition.addAnimation(enteringPage);
  rootTransition.addAnimation(leavingPage);
  return rootTransition;
}

The code uses the new Ionic Animations API which was recently officially announced. That's the main reason why it only works with Ionic 5.

However, the same APIs were already used internally in Ionic 4. If you haven't upgraded to Ionic 5 yet, you should still be able to use the above code with only minor modifications. To see what's different, you can check the source code for the default animations in the version of Ionic you're using.

The full source code for the sample project is available in a Bitbucket repository.

This blog post is a part of a series of posts about animations in Ionic.

Get notified when a new blog post is published (usually every Friday):

Copyright
Creative Commons License