7

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!

Lucas
  • 9,871
  • 5
  • 42
  • 52
Han Che
  • 8,239
  • 19
  • 70
  • 116
  • There's new Animation API in RC2, you can look into that. [See this example](https://angular.io/docs/ts/latest/guide/animations.html#!#example-entering-and-leaving). I didn't try it with router yet, but looks like fun (: – Sasxa Jun 19 '16 at 16:18
  • so the answers mainly show a animation with embedded CSS in TypeScript. Is there any examples of using ngClass so you can apply CSS styles instead for enter/leave or in/out effects? – Mark Feb 22 '18 at 20:23

2 Answers2

15

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

SpaceFozzy
  • 2,728
  • 1
  • 19
  • 21
  • 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 – David Alsh Aug 24 '17 at 04:27
  • 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. – SpaceFozzy Aug 25 '17 at 01:47
  • 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`. http://i.imgur.com/kp8mrVo.jpg – David Alsh Sep 10 '17 at 14:24
  • This will not work for --prod builds. There is a fix in but we'll have to wait for the release: https://github.com/angular/angular/issues/17467 – dale Sep 17 '17 at 15:29
  • But, how to animate transition between "products/id1" and "products/id2" ? It does not look like there is a transition defined for that case. – Evgeny Bobkin Dec 01 '17 at 13:13
  • 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. – Mark Feb 03 '18 at 03:01
  • 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 – user1059939 Jul 20 '18 at 07:42
7

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)'}))
      ])
    ])
  ]
})
Brian Clapper
  • 25,705
  • 7
  • 65
  • 65
vSimak
  • 377
  • 3
  • 6
  • 7
    A friendly note that, currently, the Plunker does not animate the transition (perhaps because of updates to angular2 from *rc_* to *stable*?) – msanford Oct 12 '16 at 13:04