How do you create reusable Animations in Angular 2

I'm playing with the Animation API, and I'd like to create a reusable animation like say sliding in content for top level router views. I managed to get through the funky meta data syntax (which is actually pretty cool once get past the crazy idea of using metadata) to get the animations mostly working.

   @Component({
      //moduleId: module.id,
      selector: 'album-display',
      templateUrl: './albumDisplay.html',
      animations: [
        trigger('slideIn', [
          state('*', style({
            transform: 'translateX(100%)',
          })),
          state('in', style({
            transform: 'translateX(0)',
          })),
          state('out',   style({
            transform: 'translateX(-100%)',
          })),
          transition('* => in', animate('600ms ease-in')),
          transition('in => out', animate('600ms ease-in'))
        ])
      ]
  })
  export class AlbumDisplay implements OnInit {
      slideInState = 'in';
      ...
  }

And then assigning that to my top level element in the component:

  <div  class="container" [@slideIn]="slideInState">

So this works, but how can I make this reusable? I don't want to stick this meta data onto every view. Since this is metadata I'm not sure how you could make this reusable.


One possible way is to put animation trigger code in separate file and export it as const variable and use it in component as below.

animations.ts

import { trigger, state, style, transition, animate } from '@angular/core';

export const slideIn = trigger('slideIn', [
  state('*', style({
    transform: 'translateX(100%)',
  })),
  state('in', style({
    transform: 'translateX(0)',
  })),
  state('out',   style({
    transform: 'translateX(-100%)',
  })),
  transition('* => in', animate('600ms ease-in')),
  transition('in => out', animate('600ms ease-in'))
]);

album-display.component.ts

import { slideIn } from './animations'; // path to your animations.ts file

@Component({
    //moduleId: module.id,
    selector: 'album-display',
    templateUrl: './albumDisplay.html',
    animations: [
      slideIn
    ]
})
export class AlbumDisplay implements OnInit {
    slideInState = 'in';
    ...
}

Maybe it's a bit late, but I'd still like to put an answer for a more dynamic version of the trigger.

Put animation trigger code in separate file and export it as function.

translate.ts

import { AnimationEntryMetadata, trigger, state, style, transition, animate } from '@angular/core';

export function TranslateX( name: string, x: string, duration: number ): AnimationEntryMetadata {
    return trigger( name, [
            state('false', style({ transform: 'translateX(0)' }) ),
            state('true',  style({ transform: 'translateX(' + x + ')' }) ),
            transition('0 => 1', animate( duration + 'ms ease-in')),
            transition('1 => 0', animate( duration + 'ms ease-out')),
        ]);
}

so, in the Component app.component.ts

import { TranslateX } from './translate';

@Component({
    ...
    templateUrl: './app.component.html',
    animations:   [ 
                    TranslateX( 'trigger1Title','-100%', 200 ),
                    TranslateX( 'trigger2Title','20vw', 700 )
                  ]
    ...
})

and in template app.component.html

...
<div [@trigger1Title]="token1"> ... </div>
<div [@trigger2Title]="token2"> ... </div>
...

You can customize the trigger with more input data, for example separating the transition times, and so on.


Router Animation Example in Angular 4

I just finished tackling router animations myself using Angular 4, here's a couple of example animations I came up with for a fade in transition and slide in/out transition.

Check out this post for more details and a live demo.

Angular 4 Slide in/out animation

// import the required animation functions from the angular animations module
import { trigger, state, animate, transition, style } from '@angular/animations';
 
export const slideInOutAnimation =
    // trigger name for attaching this animation to an element using the [@triggerName] syntax
    trigger('slideInOutAnimation', [
 
        // end state styles for route container (host)
        state('*', style({
            // the view covers the whole screen with a semi tranparent background
            position: 'fixed',
            top: 0,
            left: 0,
            right: 0,
            bottom: 0,
            backgroundColor: 'rgba(0, 0, 0, 0.8)'
        })),
 
        // route 'enter' transition
        transition(':enter', [
 
            // styles at start of transition
            style({
                // start with the content positioned off the right of the screen,
                // -400% is required instead of -100% because the negative position adds to the width of the element
                right: '-400%',
 
                // start with background opacity set to 0 (invisible)
                backgroundColor: 'rgba(0, 0, 0, 0)'
            }),
 
            // animation and styles at end of transition
            animate('.5s ease-in-out', style({
                // transition the right position to 0 which slides the content into view
                right: 0,
 
                // transition the background opacity to 0.8 to fade it in
                backgroundColor: 'rgba(0, 0, 0, 0.8)'
            }))
        ]),
 
        // route 'leave' transition
        transition(':leave', [
            // animation and styles at end of transition
            animate('.5s ease-in-out', style({
                // transition the right position to -400% which slides the content out of view
                right: '-400%',
 
                // transition the background opacity to 0 to fade it out
                backgroundColor: 'rgba(0, 0, 0, 0)'
            }))
        ])
    ]);

Angular 4 Fade in animation

// import the required animation functions from the angular animations module
import { trigger, state, animate, transition, style } from '@angular/animations';
 
export const fadeInAnimation =
    // trigger name for attaching this animation to an element using the [@triggerName] syntax
    trigger('fadeInAnimation', [
 
        // route 'enter' transition
        transition(':enter', [
 
            // css styles at start of transition
            style({ opacity: 0 }),
 
            // animation and styles at end of transition
            animate('.3s', style({ opacity: 1 }))
        ]),
    ]);

Component with transition attached

import { Component } from '@angular/core';
 
// import fade in animation
import { fadeInAnimation } from '../_animations/index';
 
@Component({
    moduleId: module.id.toString(),
    templateUrl: 'home.component.html',
 
    // make fade in animation available to this component
    animations: [fadeInAnimation],
 
    // attach the fade in animation to the host (root) element of this component
    host: { '[@fadeInAnimation]': '' }
})
 
export class HomeComponent {
}

Proper solution would be to have animations supported in Directives.

It is not the case, but there is a issue for that on Angular's Github: https://github.com/angular/angular/issues/9947

Hope it will be solved soon.


With a class and you can extended,

import { trigger, style, state, animate, transition, AnimationMetadata } from "@angular/core";
export class MyAwesomeAnimations {

    /**
     * 
     * @param nameTrigger Name of triggers
     * @param setNewsStates add states for animations
     * @param setNewTransitions add transitions for states
     */
    SetTrigger(nameTrigger: string, setNewsStates?: AnimationMetadata[], setNewTransitions?: AnimationMetadata[]): any {
        let metaData: AnimationMetadata[] = [
            state('*', style({
                transform: 'translateX(100%)',
            })),
            state('in', style({
                transform: 'translateX(0)',
            })),
            state('out', style({
                transform: 'translateX(-100%)',
            })),
            transition('* => in', animate('600ms ease-in')),
            transition('in => out', animate('600ms ease-in'))
        ];
        if (setNewsStates)
            metaData.concat(setNewsStates);
        if (setNewTransitions)
            metaData.concat(setNewTransitions);


        return trigger(nameTrigger, metaData);
    }
}

For use

    @Component({
        selector: "orden-detail-component",
        animations: [new MyAwesomeAnimations().SetTrigger("slideIn")],
        template:"<div  class="container" [@slideIn]="slideInState">"
    })
    export class OrdenDetailComponent {
       slideInState = 'in';
    }

I hope that way can help you