Angular 2 "slide in animation" of a routed component

26,387

Solution 1

With Angular 4.1 it is now possible to create specific route animations. This is different from triggering an animation when a component is displayed because it will let you animate the entering/leaving component at the same time for a smooth transition, and let you modify the transition depending on which component is coming or going. That means you can do complex transitions like slide a component in from the right if you're drilling down into content, and slide it in from the left if you're entering it via a 'back' button from another component.

  1. First, annotate your router outlet like so (eg. app.component.html):

    <div class="page" [@routerAnimations]="prepareRouteTransition(outlet)">
        <router-outlet #outlet="outlet"></router-outlet>
    </div>
    
  2. Implement the prepareRouteTransition(outlet) function in the corresponding component definition (e.g. app.component.js).

    prepareRouteTransition(outlet) {
        const animation = outlet.activatedRouteData['animation'] || {};
        return animation['value'] || null;
    }
    
  3. Define your animations (e.g. app.component.js):

      const slideLeft = [
        query(':leave', style({ position: 'absolute', left: 0, right: 0 ,transform: 'translate3d(0%,0,0)' }), {optional:true}),
        query(':enter', style({ position: 'absolute', left: 0, right: 0, transform: 'translate3d(-100%,0,0)' }), {optional:true}),
        group([
          query(':leave', group([
            animate('500ms cubic-bezier(.35,0,.25,1)', style({ transform: 'translate3d(100%,0,0)' })), // y: '-100%'
          ]), {optional:true}),
          query(':enter', group([
            animate('500ms cubic-bezier(.35,0,.25,1)', style({ transform: 'translate3d(0%,0,0)' })),
          ]), {optional:true})
        ])
      ]
    
      const slideRight = [
        query(':leave', style({ position: 'absolute', left: 0, right: 0 , transform: 'translate3d(0%,0,0)'}), {optional:true}),
        query(':enter', style({ position: 'absolute', left: 0, right: 0, transform: 'translate3d(100%,0,0)'}), {optional:true}),
    
        group([
          query(':leave', group([
            animate('500ms cubic-bezier(.35,0,.25,1)', style({ transform: 'translate3d(-100%,0,0)' })), // y: '-100%'
          ]), {optional:true}),
          query(':enter', group([
            animate('500ms cubic-bezier(.35,0,.25,1)', style({ transform: 'translate3d(0%,0,0)' })),
          ]), {optional:true})
        ])
      ]
    
  4. Add the animation metadata to your route definitions (e.g. app.routing.ts):

    const routes: Routes = [
      {
        path: 'products',
        component: ProductsComponent,
        data: {
          animation: {
            value: 'products',
          }
        }
      },
      {
        path: 'products/:id',
        component: ProductDetailComponent,
        data: {
           animation: {
            value: 'product-detail',
          }
        }
      }
    
  5. Finally, register a 'routerAnimations' animation trigger on your component with the animations and route metadata you defined (e.g. app.component.js):

    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css'],
      animations: [
        trigger('routerAnimations', [
          transition('products => product-detail', slideRight),
          transition('product-detail => products', slideLeft),
        ])
      ]
    })
    

Don't forget to polyfill the Web Animation API to target old browsers

Matias Niemela talks more about route animations at ng-conf here (with a demo): https://youtu.be/Oh9wj-1p2BM?t=12m21s

His presentation code: https://github.com/matsko/ng4-animations-preview

Solution 2

In terms of sliding in it is quite straightforward.

You can reference to the Official Angular 2 Animate docs.

You can also check out this Plunker I did for a simple showcase, using the new router v3

Bear in mind that I am struggling to figure out how to actually have the leave/exit/void transitions when the triggered element is about to be destroyed from the view.

I opened another thread in Angular 2 Animate - No visible effect of the '* => void' transition when changing routes/components to try to figure out how to make router take notice of the leaving animation/transition time.

@Component({
  selector: 'home',
  directives: [ROUTER_DIRECTIVES],
  template: `
  <div @flyInOut="'active'" class="radibre">
  </div>
  `,
  styles: ['.radibre { width: 200px; height: 100px; background: red; }'],
  animations: [
    trigger('flyInOut', [
      state('in', style({transform: 'translateX(0)'})),
      transition('void => *', [
        style({transform: 'translateX(-100%)'}),
        animate(100)
      ]),
      transition('* => void', [
        animate(100, style({transform: 'translateX(100%)'}))
      ])
    ])
  ]
})

export class Home {
  constructor() { }
}
@Component({
  selector: 'page',
  template: `
  <div @testingBottom="'active'" class="page"></div>`,
  styles: ['.page { width: 300px; height: 50px; background: green; }'],
  animations: [
    trigger('testingBottom', [
      state('active', style({transform: 'scale(1)'})),
      transition('void => *', [
        style({transform: 'scale(0)'}),
        animate(100)
      ]),
      transition('* => void', [
        animate(100, style({transform: 'scale(0)'}))
      ])
    ])
  ]
})
Share:
26,387
Han Che
Author by

Han Che

Updated on June 21, 2020

Comments

  • Han Che
    Han Che about 4 years

    let's say I have 2 routed components and two Routerlinks in the fixed navbar to route them. I want them to slide in from the right when I click the Routerlinks.

    I don't want to offset the component with css and use a timeout function to change the css class to let it slide in (e.g. with ngStyle or ngClass).

    are there any more elegant ways do achieve that in Angular 2?

    Thanks!

  • msanford
    msanford over 7 years
    A friendly note that, currently, the Plunker does not animate the transition (perhaps because of updates to angular2 from rc_ to stable?)
  • David Alsh
    David Alsh almost 7 years
    I can't seem to build my Angular project after doing this. It seems the github project can't build at all out of the box either
  • SpaceFozzy
    SpaceFozzy almost 7 years
    Hmm, was just able to clone, build and run the github project out of the box on two different laptops. If you're still having trouble, can you create a new question and post some code or post your project? Would love to help.
  • David Alsh
    David Alsh almost 7 years
    Hey thanks for the response, this is what happens on my computer with the latest angular CLI & node if I run ng build --aot --prod. i.imgur.com/kp8mrVo.jpg
  • dale
    dale almost 7 years
    This will not work for --prod builds. There is a fix in but we'll have to wait for the release: github.com/angular/angular/issues/17467
  • Evgeny Bobkin
    Evgeny Bobkin over 6 years
    But, how to animate transition between "products/id1" and "products/id2" ? It does not look like there is a transition defined for that case.
  • Mark
    Mark over 6 years
    There has to be away to do this without hardcoding CSS values in TypeScript, like maybe listening to a 'animationEnd' on a element. I get animate might help a bunch pin a bunch of animations together like GSAP but I thought we weren't doing CSS animations in JavaScript anymore.
  • user1059939
    user1059939 almost 6 years
    Just a quick note that products is re-used by default. You need to supply a RouteReuseStrategy which tells Angular to create multiple product then you can animate them. However the rest of Animation is as cryptic as you say