Angular 2 “slide in animation” of a routed compone

2019-01-11 11:49发布

问题:

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!

回答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



回答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)'}))
      ])
    ])
  ]
})