Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Animate your Angular apps

Animate your Angular apps

Animations are always exciting, in this presentation I have shown how much fun it is to create them with Angular.

Play around with the demo: https://stackblitz.com/edit/zen-feedback?embed=1&file=app/app.component.ts&view=preview

Ciro Nunes

March 29, 2018
Tweet

More Decks by Ciro Nunes

Other Decks in Programming

Transcript

  1. Animations in JavaScript? Why not just CSS? Dynamic elements, page

    transitions Animate independently of the amount of elements, animate routes
  2. Animations in JavaScript? Why not just CSS? Dynamic elements, page

    transitions Programmatic animations Animate independently of the amount of elements, animate routes Full control over the logic of the animations
  3. How can I animate in JS? Declarative Bind animations to

    the template Imperative Trigger animations programmatically
  4. Agenda What are we going to learn today? Basic and

    multi-step 1 Dynamic # of items 2
  5. Agenda What are we going to learn today? Basic and

    multi-step 1 Dynamic # of items 2 Page transitions 3
  6. Agenda What are we going to learn today? Basic and

    multi-step 1 Dynamic # of items 2 Page transitions 3 Programatic animations 4
  7. Create a project using the CLI ~ $ npm i

    -g @angular/cli ~ $ ng new my-project
  8. ... import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; @NgModule({ imports: [

    BrowserModule, BrowserAnimationsModule ] }) export class AppModule {} Setup app.module.ts
  9. ... import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; @NgModule({ imports: [

    BrowserModule, BrowserAnimationsModule ] }) export class AppModule {} Setup app.module.ts NoopAnimationsModule Protip: for tests use:
  10. Enable the polyfill Hot of the press: in v6 it

    fallbacks to CSS Keyframes for you!
  11. import { trigger, ... } from '@angular/animations'; export const fadeAnimation

    = trigger('fade', [ state('inactive', style({ opacity: 0 })), state('active', style({ opacity: 1 })), transition('inactive <=> active', animate('450ms ease-in')) ]); Register the trigger fade.animation.ts
  12. import { state, style, ... } from '@angular/animations'; export const

    fadeAnimation = trigger('fade', [ state('inactive', style({ opacity: 0 })), state('active', style({ opacity: 1 })), transition('inactive <=> active', animate('450ms ease-in')) ]); Setup states fade.animation.ts
  13. import { transition, animate, ... } from '@angular/animations'; export const

    fadeAnimation = trigger('fade', [ state('inactive', style({ opacity: 0 })), state(‘active', style({ opacity: 1 })), transition('inactive <=> active', animate('450ms ease-in')) ]); Setup transitions fade.animation.ts
  14. import { fadeAnimation } from './fade.animation'; @Component({ ..., animations: [fadeAnimation]

    }) export class HelloWorldComponent {} Import the animation hello.component.ts
  15. import { fadeAnimation } from './fade.animation'; @Component({ ..., animations: [fadeAnimation]

    }) export class HelloWorldComponent {} Include it hello.component.ts
  16. ... export class HelloWorldComponent { cmpState = 'inactive'; toggle() {

    this.cmpState = 'active'; } } Change state hello.component.ts
  17. export interface AnimationEvent { fromState: string; toState: string; totalTime: number;

    phaseName: string; element: any; triggerName: string; } AnimationEvent animation_event.ts
  18. animate('5s', keyframes([ style({ backgroundColor: 'red', offset: 0 }), style({ backgroundColor:

    'blue', offset: 0.2 }), style({ backgroundColor: 'orange', offset: 0.3 }), style({ backgroundColor: 'black', offset: 1 }) ])); Keyframes multistep.animation.ts
  19. animate('5s', keyframes([ style({ backgroundColor: 'red', offset: 0 }), style({ backgroundColor:

    'blue', offset: 0.2 }), style({ backgroundColor: 'orange', offset: 0.3 }), style({ backgroundColor: 'black', offset: 1 }) ])); Keyframes multistep.animation.ts
  20. transition('inactive => active’, [ query('.container .item', [ style({ opacity: 0,

    transform: 'translateY(-50px)' }), stagger(100, [ animate('500ms', style({ opacity: 1, transform: 'none' })) ]) ]) ]) Setup the transition appear.animation.ts
  21. transition('inactive => active', [ query('.container .item', [ style({ opacity: 0,

    transform: 'translateY(-50px)' }), stagger(100, [ animate('500ms', style({ opacity: 1, transform: 'none' })) ]) ]) ]) Query the DOM appear.animation.ts
  22. transition('inactive => active', [ query('.container .item', [ style({ opacity: 0,

    transform: 'translateY(-50px)' }), stagger(100, [ animate('500ms', style({ opacity: 1, transform: 'none' })) ]) ]) ]) Initial styles appear.animation.ts
  23. transition('inactive => active', [ query('.container .item', [ style({ opacity: 0,

    transform: 'translateY(-50px)' }), stagger(100, [ animate('500ms', style({ opacity: 1, transform: 'none' })) ]) ]) ]) Animate one by one appear.animation.ts
  24. <div [@routeAnimation]="getDepth(myOutlet)"> <router-outlet #myOutlet="outlet"></router-outlet> </div> Bind to the animation route.animation.ts

    Protip: you’ll want to use CSS to make your container use the whole page position: absolute; top: 0; right: 0; left: 0;
  25. Routes config route.animation.ts const ROUTES: Routes = [ { path:

    '', component: HomeComponent, data: { page: '1' } }, { path: 'about', component: AboutComponent, data: { page: '2' } } ];
  26. Routes config route.animation.ts const ROUTES: Routes = [ { path:

    '', component: HomeComponent, data: { page: '1' } }, { path: 'about', component: AboutComponent, data: { page: '2' } } ];
  27. transition('1 => 2', [ style({ height: '!' }), query(':enter', style({

    transform: 'translateX(-100%)' })), group([ query(':leave', animate('.6s cubic-bezier(…)', style({ transform: 'translat query(':enter', animate('.6s cubic-bezier(…)', style({ transform: 'translat ]) ]) Initial styles. Notice the '!' route.animation.ts
  28. transition('1 => 2', [ style({ height: '!' }), query(':enter', style({

    transform: 'translateX(-100%)' })), group([ query(':leave', animate('.6s cubic-bezier(…)', style({ transform: 'translat query(':enter', animate('.6s cubic-bezier(…)', style({ transform: 'translat ]) ]) Initial styles route.animation.ts
  29. transition('1 => 2', [ style({ height: '!' }), query(':enter', style({

    transform: 'translateX(-100%)' })), group([ query(':leave', animate('.6s cubic-bezier(…)', style({ transform: 'translat query(':enter', animate('.6s cubic-bezier(…)', style({ transform: 'translat ]) ]) Initial styles route.animation.ts Protip: :enter is just an alias for void => *
  30. transition('1 => 2', [ style({ height: '!' }), query(':enter', style({

    transform: 'translateX(-100%)' })), group([ query(':leave', animate('.6s cubic-bezier(…)', style({ transform: 'translateX(100%)' }))), query(':enter', animate('.6s cubic-bezier(…)', style({ transform: 'translateX(0)' }))) ]) ]) Parallelise with group route.animation.ts
  31. transition('1 => 2', [ style({ height: '!' }), query(':enter', style({

    transform: 'translateX(-100%)' })), group([ query(':leave', animate('.6s cubic-bezier(…)', style({ transform: 'translateX(100%)' }))), query(':enter', animate('.6s cubic-bezier(…)', style({ transform: 'translateX(0)' }))) ]) ]) Parallelise with group route.animation.ts Protip: :leave is just an alias for * => void
  32. transition('1 => 2', [ style({ height: '!' }), query(':enter', style({

    transform: 'translateX(-100%)' })), group([ query(':leave', animate('.6s cubic-bezier(…)', style({ transform: 'translateX(100%)' }))), query(':enter', animate('.6s cubic-bezier(…)', style({ transform: 'translateX(0)' }))) ]) ]) Parallelise with group route.animation.ts
  33. export class StoriesComponent { @Input('position') set position(pos: number) { this.pause();

    this.player.setPosition(pos / 100); } play() { this.player.play(); } pause() { this.player.pause(); } } stories.component.ts
  34. export class StoriesComponent { @Input('position') set position(pos: number) { this.pause();

    this.player.setPosition(pos / 100); } play() { this.player.play(); } pause() { this.player.pause(); } } stories.component.ts
  35. constructor(private builder: AnimationBuilder, private element: ElementRef) { const animation =

    this.builder.build([...]); const player = animation.create(this.element.nativeElement); } Inject builder and element stories.component.ts
  36. constructor(private builder: AnimationBuilder, private element: ElementRef) { const animation =

    this.builder.build([...]); const player = animation.create(this.element.nativeElement); } Build the animation stories.component.ts
  37. constructor(private builder: AnimationBuilder, private element: ElementRef) { const animation =

    this.builder.build([...]); const player = animation.create(this.element.nativeElement); } Create the player stories.component.ts
  38. Slides of this talk: cnun.es/animations Interactive Angular animation guide: animationsftw.in

    Links Where to go? My website: cironunes.com Docs: next.angular.io/guide/animations