slides.com/gerardsans |  @gerardsans

Reactive

Animations

Google Developer Expert

Master of Ceremonies

Blogger

International Speaker

Spoken at 56 events in 18 countries

Angular Trainer

Community Leader

900

1K

Angular In Flip Flops

#dfua

Why use Animations?

Improved UX

Perceived Performance

Immersive interactions

Better engagement

User happiness 😃

Animation Principles

Squash & Stretch + Anticipation

Follow through

Exaggeration

Composition

Animation Techniques

Move

transform: translateX(10px);

Resize

transform: scale(0.5);

Rotate

transform: rotate(1turn);

Fade

opacity: 0;

Move

<div class="circle base elastic"></div>

.base {
  animation: move 2s infinite;
}
.elastic { 
  animation-timing-function: 
    cubic-bezier(.8,-0.6,0.2,1.5);
}

@keyframes move {
  0% { 
    transform: translateX(-250px);
  }
  40%, 60% { 
    transform: translateX(50px);
  }
  100% { 
    transform: translateX(250px);
  }
}

Rotate

<div class="square base linear"></div>

.base {
  animation: spin 2s infinite;
}
.linear {
  animation-timing-function: linear;
}

@keyframes spin {
  to { 
    transform: rotate(1turn);
  }
}

Resize

<div class="circle base"></div>

.base {
  animation: size 2s infinite;
}

@keyframes size {
  0%, 40%, 100% {
    transform: scale(1);
  }
  25%, 60% {
    transform: scale(1.1);
  }
}

Fade

<div class="circle base elastic"></div>

.base {
  animation: fade 2s infinite;
}

@keyframes fade {
  0% { 
    transform: translateX(0px);
    opacity: 0;
  }
  40%, 60% { 
    transform: translateX(80px);
    opacity: 1;
  }
  100% { 
    transform: translateX(0px);
    opacity: 0;
  }
}

Advanced Techniques

Easing

Offset Delay

Parenting

Binding Values

Overlay

Example

Angular Animations

States & Transitions

void

*

Special Keywords

void => *           :enter

* => void          :leave

void <=> *

STATE

STATE

fadeIn

fadeOut

States & Transitions

TRANSITIONS

STATE

STATE

fadeIn => fadeOut

fadeOut => fadeIn

fadeIn <=> fadeOut

Animation Example

import {fade} from './animations';

@Component({
  selector: 'my-app',
  template: `<button [@fade]='fade' (click)="toggleFade()">Fade</button>`,
  animations: [ fade ]
})
export class App {
  fade = 'fadeIn';
  toggleFade(){
    this.fade = this.fade === 'fadeIn' ? 'fadeOut' : 'fadeIn';
  }
}

Animation Example

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

export const fade = trigger('fade', [
  state('fadeIn', style({ opacity: 1 })),
  state('fadeOut', style({ opacity: 0.1 })),
  transition('fadeIn <=> fadeOut', animate('2000ms linear'))
]);

Execution Order

sequence

group

time

Composition

animateChild

time

Stagger

time

Dynamic Selectors

class="container"

class="item"

class="item"

query(selector)

Dynamic Selectors

query('.item')

class="container"

class="item"

class="item"

Dynamic Selectors

query('.item:enter')

class="container"

class="item"

class="item"

class="item"

added

Reactive Animations

Introduction

  • react to user interactions
  • maps user input to CSS
    • mouse position, keyboard, scroll, input field
  • static or dynamic

Technologies

  • Animations
    • CSS, canvas, WebGL, SVG
  • AnimationBuilder
    • Player
  • Events
    • RxJS

AnimationBuilder

import {AnimationBuilder} from "@angular/animations";
export class MyComponent {
  constructor(
    private elem: ElementRef, 
    private builder: AnimationBuilder
  ) {
    const player = this.playerMoveTo({ x: 100, y: 100 });
    player.play();
  } 
  playerMoveTo(to) {
    const moveAnimation = this.builder.build([
      animate('1s', style({ transform: `translate(${x}px, ${y}px)` }))
    ]);
    return moveAnimation.create(this.elem.nativeElement
      , {params: { x: to.x, y: to.y }});
  }
}

Tracking mouse position

constructor(
  private elem: ElementRef
) {
  // find out center
  this.mx = elem.nativeElement.offsetWidth/2;
  this.my = elem.nativeElement.offsetHeight/2;
} 

ngOnInit() {
  const mouseMove$ = Observable.fromEvent(document, 'mousemove')
    .map(e => ({ x: e.pageX, y: e.pageY }));

  mouseMove$.subscribe({
    next: e => {
      this.lastPosition = { x: e.x-this.mx, y: e.y-this.my };
      const player = this.playerMoveTo(this.lastPosition);
      player.play();
    }
  })
}

Performance

Considerations

  • Avoid unnecessary layout/repaint
    • Use opacity, transform
  • Hardware acceleration (avoid)
    • transform: translateZ(0)

Saving Resources

.visible.advert {
  will-change: transform;
}

.visible.advert:hover {
  transform: scale(1.2);
}

More

Inspiration

David Khourshid

Issara Willenskomer

Sarah Drasner

Rachel Nabors