Angular Animations

@jurisicmarko

marko.jurisic@bearingpoint.com

Animations

  • engage with users, call attention where it's needed
  • make our applications more fun to use and program
  • CSS3 - adding and removing classes, good for simple effects
  • Angular Animations uses Web animations - allows for fine animation control

Angular Animations

  • Web Animations (supported in all modern browsers, polyfill available for others)
  • Animation DSL
    • states, transitions, transformations
    • from version 4.2: router animations, animation params, scrubbing...

Most Angular Docs/Tutorials

Animations docs

Minimal example

"dependencies": {
    ...
    "@angular/animations": "4.4.3",
    "@angular/platform-browser": "4.4.3"
    ...
}
 imports: [
    BrowserModule,
    BrowserAnimationsModule,
    ...
 ]
<div [@animationTrigger]="animationState">Wow! Just wow!</div>
<button (click)="doToggle()">Magic!</button>

package.json

app.module.ts

app.component.html

Minimal example

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

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  animations: [
    trigger('animationTrigger', [
      state('inactive', style({ opacity: 0 })),
      state('active', style({ opacity: 1 })),

      transition('inactive=>active', animate(1500))
    ])
  ]
})
export class AppComponent {
  animationState = 'inactive'
  doToggle() {
    this.animationState = this.animationState == 'active' ? 'inactive' : 'active';
  }
}

app.component.ts

Demo

States

  • string values, tied to a class property
  • special states void and *

Transitions

  • Using predefined states:


     
  • Inline transitions:




     
transition('void => *', [
      style({transform: 'translateX(-100%)'}),
      animate(100)
    ]),
state('inactive', style({ opacity: 0 })),
state('active', style({ opacity: 1 })),
transition('inactive <=> active', animate(1500))
transition(':enter', [ ... ]); // void => *
transition(':leave', [ ... ]); // * => void

Angular 4.2+ new features

  • Animation options/ input params
  • animation() / useAnimation()
  • query() and stagger()
  • sub animations using animateChild()
  • routing animations
  • programmatic animations with AnimationsBuilder

Animation options / input params

<div [@animation]="{value: expVal, /* option1: value, option2: value */ }">...</div>
// inside of @Component.animations...
transition('* => *', [
  style({ opacity: 0 }),
  animate("{{ time }}",
    style({ opacity: "{{ opacity }}" }),
], {
  time: "1s",
  opacity: "1"
})

*shamelessly stolen from https://www.yearofmoo.com/2017/06/new-wave-of-animation-features.html#animation-options-input-params

animation() / useAnimation()

// animations.ts
import {animation, style, animate} from "@angular/animations";
export var fadeAnimation = animation([
  style({ opacity: "{{ from }}" }),
  animate("{{ time }}", style({ opacity: "{{ to }}" }))
], { time: "1s" })
// inside of @Component.animations...
import {useAnimation, transition} from "@angular/animations";
import {fadeAnimation} from "./animations";
transition('* => *', [
  useAnimation(fadeAnimation, {
    from: 0,
    to: 1,
    time: '1s'
  })
])

*shamelessly stolen from https://www.yearofmoo.com/2017/06/new-wave-of-animation-features.html#animation-options-input-params

query() and stagger()

<p>Template</p>
<div [@listAnimation]="items.length">
  <div *ngFor="let item of items"  >  
    <p>{{item.title}}</p>
  </div>
</div>
// inside of @Component.animations...
trigger('listAnimation', [
        transition('*<=>*', [
          query('p', style({transform: 'translateX(-100%)'})),
          query('p', 
            stagger('200ms', [
              animate('1000ms', style({transform:'translateX(0)'}))
            ]))
        ])
    ])

//...

items = [
    {title:'first'},
    {title:'second'},
    {title:'third'},
  ]

sub animations using animateChild()

<!-- do these two guys animate at the same time? -->
<div [@parent]="parentVal">
  <div [@child]="childVal">
    ...
  </div>
</div>
trigger('parent', [
  style({ transform: 'translate(-100px)' }),
  animate('500ms',
    style({ transform: 'translate(0px)' })),
  query('@child', animateChild())
]),
trigger('child', [
  style({ opacity:0 }),
  animate('0.5s', style({ opacity:1 }))
])

*shamelessly stolen from https://www.yearofmoo.com/2017/06/new-wave-of-animation-features.html#animation-options-input-params

trigger('parent', [
  style({ transform: 'translate(-100px)' }),
  animate('500ms', style({ transform: 'translate(0px)' }))
]),
trigger('child', [
  style({ opacity:0 }),
  animate('0.5s', style({ opacity:1 }))
])

Parent always wins, unlike in real world

routing animations

<!-- app.html -->
<div [@routeAnimation]="prepRouteState(routerOutlet)">
  <!-- make sure to keep the ="outlet" part -->
  <router-outlet #routerOutlet="outlet"></div>
<div>
trigger('routeAnimation', [
  //...
  transition('homePage => supportPage', [
    // make sure the new page is hidden first
    query(':enter', style({ opacity: 1 })),
    // animate the leave page away
    query(':leave', [
      animate('0.5s', style({ opacity: 0 })),
      style({ display: 'none' })
    ]),
    // and now reveal the enter
    query(':enter', animate('0.5s', 
       style({ opacity: 1 }))),
  ]),
  //...
])

*shamelessly stolen from https://www.yearofmoo.com/2017/06/new-wave-of-animation-features.html#animation-options-input-params

<!-- app.ts -->
const ROUTES = [
  { path: '',
    component: HomePageComponent,
    data: {
      animation: 'homePage'
    }
  },
  { path: 'support',
    component: SupportPageComponent,
    data: {
      animation: 'supportPage'
    }
  }
]

programmatic animations with AnimationsBuilder

 constructor(private _builder: AnimationBuilder) {}

 @Input('percentage')
  set percentage(p: number) {
    this._percentage = p;
    
    if (this.player) {
      this.player.destroy();
    }
    
    const factory = this._builder.build([
      style({ width: '*' }),
      animate('350ms cubic-bezier(.35, 0, .25, 1)', style({ width: (p * 100) + '%' }))
    ]);
    this.player = factory.create(this.loadingBar.nativeElement, {});
    this.player.play();
  }

*shamelessly stolen from https://www.yearofmoo.com/2017/06/new-wave-of-animation-features.html#animation-options-input-params

Another demo

References

Angular Animations

By Marko Jurišić