3

I've been trying out about 5 implementations of router-outlet animations. I just want a basic fade-in/fade-out animation when the route changes, nothing fancy.

Here's a screen-recording of how things look when I switch route: https://streamable.com/tbkxt

This is by far the best result I've obtained, in other cases I just had buttons/text disappear off the screen one by one. I'm testing in Chrome.

I have sidenav navigation enabled and my code looks like this:

<mat-sidenav-content [@routerTransition]="getPageTransition(routerOutlet)">
      <button
        @menu-button
        *ngIf="!snav.opened"
        mat-button
        (click)="snav.toggle()"
        class="navigation-toggle"
      >
        <fa-icon icon="bars"></fa-icon>
      </button>

      <button
        @menu-button
        *ngIf="snav.opened"
        mat-button
        (click)="snav.toggle()"
        class="navigation-toggle"
      >
        <fa-icon icon="times"></fa-icon>
      </button>
      <router-outlet #routerOutlet="outlet"></router-outlet>
    </mat-sidenav-content>

My animations are defined:

const query = (style, animate, optional = { optional: true }) =>
  q(style, animate, optional);

const fade = [
  query(':enter, :leave', style({ position: 'fixed', width: '100%' })),
  query(':enter', [style({ opacity: 0 })]),
  group([
    query(':leave', [animate('0.3s ease-out', style({ opacity: 0 }))]),
    query(':enter', [
      style({ opacity: 0 }),
      animate('0.3s ease-out', style({ opacity: 1 }))
    ])
  ])
];

export const routerTransition = trigger('routerTransition', [
  transition('void => *', [
    style({ top: '0px', opacity: 0 }),
    animate(2000, style({ top: '0px', opacity: 1 }))
  ]),
  transition('* => void', [
    style({ top: '0px', opacity: 1 }),
    animate(2000, style({ top: '0px', opacity: 0 }))
  ]),
  transition('* => forward', fade),
  transition('* => backward', fade)
]);

And in the component I check the router outlet:

 getPageTransition(routerOutlet: RouterOutlet) {
    if (routerOutlet.isActivated) {
      let transitionName = 'section';

      const { path } = routerOutlet.activatedRoute.routeConfig;
      const isSame = this.previousPath === path;
      const isBackward = this.previousPath.startsWith(path);
      const isForward = path.startsWith(this.previousPath);

      if (isSame) {
        transitionName = 'none';
      } else if (isBackward && isForward) {
        transitionName = 'initial';
      } else if (isBackward) {
        transitionName = 'backward';
      } else if (isForward) {
        transitionName = 'forward';
      }

      this.previousPath = path;

      return transitionName;
    }
SebastianG
  • 8,563
  • 8
  • 47
  • 111
  • Hi Sebastian, I also had bad experiences with router animations in the past. One always repetetive bug had been issous with the following: https://github.com/angular/angular/issues/15477. There are several solutions and it might help you. As far as I understood you need to hold the component by animating the router from 0.99999 opacity to 1 to hold the whole content and therefore the animation alive until the component can be thrown away. – Jonathan Stellwag Mar 20 '19 at 13:39
  • @JonathanStellwag thank you -- I'm not 100% sure I understand what you mean by animating the router from 0.99999 to 1 --- you mean on entry I should start from 0.99999 instead of 0? wouldn't that keep the whole thing visible? or to animate it from 0 to 0.99999 -- looking at the idea of 0.99999 to 1 it will most likely just disappear in a blink with no actual fade, but I think I'm misunderstanding. Thanks! – SebastianG Mar 20 '19 at 14:21
  • Hi @SebastianG. I will write you an answer that might needs to be adapted to your case, and/or you have to try and error a bit. But code in comment section is not nice :) – Jonathan Stellwag Mar 21 '19 at 12:35

1 Answers1

0

I figured out that :enter and :leave events close to routing can cause several issues with angular animation. This answer is not testet for your case and might needs to be adapted, because the following snippet depend on your application structure and your nested router. I will adapt this answer if it doesn't fit or solve your issue!

Regarding to this Github Issue child routes disappear immediately without playing animations when the parent route changes.

The following comment solved my issue but i had to adapt it for my environment:

let animation = [...]; // the metadata
transition('A => B', group([
  query(':self', animation),
  query('router-outlet ~ *', [style({}), animate(1, style({}))], { optional: true })
]))

That in fact means that you do not only animate your element. You also apply an animation to the router-outlet itself. It could be implemented for you code as the following:

query('mat-sidenav-content > router-outlet ~ *', [
  style({
  opacity: .99999
}),
animate(yourAnimationDuration, style({
  opacity: 1
}))
], { optional: true });

I am no Css-Selector professional you might need to adapt the

mat-sidenav-content > router-outlet ~ *

I hope this answer can help you :)

Jonathan Stellwag
  • 3,843
  • 4
  • 25
  • 50