Search code examples
angularcss-animationsangular7angular-routerangular-animations

Angular7 router-outlet animations skews and rearranges all elements making a mess


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

Solution

  • 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 :)