0

I have an Ionic (v6) Angular (v15) project where I'd like to have custom page transitions that are split into 3 parts:

  1. The current page blurs
  2. The current page performs some infinite loading cycle (When I try to use blur or opacity here, the whole thing breaks) that will indicate to the user that the page is loading
  3. The current page fades out, and the next page fades in at the same time

1, and 2 are started right away (and 2 is delayed by 1's duration). 3 awaits a signal from the next page before it can start.

All pages that will employ this strategy will extend NavigableComponent

Here's the page transition animation:

import { AnimationController, Animation, AnimationBuilder } from '@ionic/angular';

export const PAGE_FADE_DURATION: number = 500;

export const pageTransitionAnimation: AnimationBuilder = (baseEl: HTMLElement, opts?: any): Animation => {

    const animationCtrl = new AnimationController();

    const leaveDuration: number = 500;
    const loadingDuration: number = 500;

    const blurLeaving = animationCtrl.create()
        .addElement(opts.leavingEl)
        .duration(leaveDuration)
        .easing('ease')
        .fromTo('filter', 'blur(0px)', 'blur(5px)')

    const loadingEntering = animationCtrl.create()
        .addElement(opts.leavingEl)
        .delay(leaveDuration)
        .duration(loadingDuration)
        .easing('ease')
        .iterations(Infinity)
        .direction('alternate')
        .fromTo('left', '0px', '0px')
        
    const fadeLeaving = animationCtrl.create()
        .addElement(opts.leavingEl)
        .duration(PAGE_FADE_DURATION)
        .easing('ease')
        .fromTo('opacity', 1, 0)

    const showEntering = animationCtrl.create()
        .addElement(opts.enteringEl)
        .duration(PAGE_FADE_DURATION)
        .easing('ease')
        .fromTo('opacity', 0, 1)

    const animationUntilLoaded = animationCtrl.create().addAnimation([ blurLeaving, loadingEntering ]);

    opts.enteringEl.__data = { 
        __ON_ENTER_ANIMATION: animationCtrl.create().addAnimation([ fadeLeaving, showEntering ]),
        __ON_LEAVE_ANIMATION: animationUntilLoaded,
    };

    return animationUntilLoaded;
} 

Here's the Navigable component:

import { ElementRef, inject } from "@angular/core";
import {  PopoverController, Animation } from "@ionic/angular";
import { LoggableComponent } from "./loggable-component";
import { ReplaySubject } from 'rxjs';
import { PAGE_FADE_DURATION } from "../animations/page-transition.animation";

export abstract class NavigableComponent extends LoggableComponent {

    setReady() {
        this._onReadySubject.next();
    }
    onReady: () => Promise<void> = async () => {
        return new Promise((resolve, reject) => {
            this._onReadySubject.subscribe(() => {
                resolve();
            });
        });
    };

    private _onReadySubject: ReplaySubject<void> = new ReplaySubject();

    private _el: ElementRef = inject(ElementRef);

    constructor(
        title: string,
        enabled: boolean,
        popoverController: PopoverController,
    ) {
        super(title, enabled, popoverController);
        this._el.nativeElement.__COMPONENT = this;
    }

    async ionViewWillEnter() {
        if (this._el.nativeElement.__data) {
            const onEnterAnimation = this._el.nativeElement.__data.__ON_ENTER_ANIMATION as Animation | undefined;
            const onLeaveAnimation = this._el.nativeElement.__data.__ON_LEAVE_ANIMATION as Animation | undefined;

            await this.onReady();

            if (onEnterAnimation) {
                onEnterAnimation.play();
            }

            if (onLeaveAnimation) {
                setTimeout(() => {
                    onLeaveAnimation.iterations(0);
                    onLeaveAnimation.stop();
                    onLeaveAnimation.destroy();
                }, PAGE_FADE_DURATION);
            }                

        }
            
    }

}

And here's the code each Component derived from NavigableComponent will have:

  async ngAfterViewInit() {
    await < ... some promise once page is ready >;
    this.setReady();
  }

This works for any first transition after I first load the app, but fails afterwards. I'm assuming this is because the Animation.iterations(Infinity) screws up the Ionic page lifecycle. Does anyone know how I can accomplish this? Am I on the right track?

There seems to be very spotty documentation on animating page transitions.

Any help would be appreciated.

Jeremy Friesen
  • 383
  • 1
  • 3
  • 13

0 Answers0