ng2 + ngAnimate

yom.nu/ng-conf-2016-slides

Matias Niemelä

ng-conf 2016
www.yearofmoo.com
@yearofmoo

Let’s talk animation …

What are our options?

CSS Transitions

CSS Keyframes

The WebAnimations API

Regular JavaScript

CSS Transitions

Element state changes

Auto style application

Controllable

Programmatic Access

CSS Keyframes

Animation Sequences

Simple, but limited

All styles required

Programmatic Access

Web Animations

Programmatic Access

All styles required

No stylesheet access

Browser Support

What ng1 Did

Lots of CSS

Some JavaScript

The browser knows all

DOM States

ng2 Does it differently

No CSS transitions/keyframes

Yes Web Animations

Borrows the philosophy

Timeline-based

Component States

Demo Code

All code is available at:

github.com/matsko/ng-conf-demos

Note: Old/new API

The Basics

<div @myAnimation="expression"> ... </div>
@Component({ animations: [ animation('myAnimation', [ transition('state1 => state2', [ style({ opacity: 0 }), animate('1s', [ style({ opacity: 1 }) ]) ]) ]) ] })
// hello-world.html <button (click)="toggle()">Show/Hide</button> <div @myAnimation="isOpen ? 'open' : 'closed'">> hello world </div> // hello-world.ts import { Component, state, animation, transition, style, animate } from '@angular/core'; @Component({ selector: 'hello-world', animations: [ animation('myAnimation', [ state('open', style({ opacity: 1 })), state('closed', style({ opacity: 0 })), transition('open => closed', animate(500)), transition('closed => open', animate('0.5s')) ]) ] }) class HelloWorldCmp { isOpen = false; toggle() { this.isOpen = !this.isOpen; } }

Component UI States

We use the "@" sign

Persisted styling

The Arc animates in between

1

<div class="left"> <div class="email-list-item" *ngFor="let email of emails" [class.active]="email === selectedEmail" (click)="selectedEmail = email"> <div class="background" @state="email === selectedEmail ? 'active' : 'hidden'"></div> <h1>{{ email.subject }}</h1> <h2>{{ email.sender }}</h2> </div> </div> <div class="right"> <div class="email-full" *ngFor="let email of emails" @state="email === selectedEmail ? 'active' : 'hidden'"> <h1>{{ email.subject }}</h1> <h2>From {{ email.sender }}</h2> <hr> <div class="email-body"> <pre>{{ email.body }}</pre> </div> </div> </div> @Component({ selector: 'my-app', templateUrl: 'app/app.html', animations: [ animation('state', [ state('void', style({ display: 'none' })), state('active', style({ transform: 'translate3d(0, 0, 0)' })), state('hidden', style({ transform: 'translate3d(100%, 0, 0)' })), transition('active => hidden', [animate('350ms ease-out')]), transition('hidden => active', [ style({ transform: 'translate3d(-100%, 0, 0)' }), animate('350ms ease-out') ]), ]), ] }) export class App {...}

Performance

No getComputedStyle

Web Animations integration

Code Generation

Web Workers

Web-Animations Polyfill

Use the w3c version today

https://github.com/web-animations/web-animations-js

Angular will have a lightweight version soon

https://github.com/angular/element-animate-polyfill

Styles Styles Styles

How can we apply style?

Is everything inline?

Can I use classes?

Keyframes?

Programmatic styling?

@Component({ // define your styling styleUrls: ['animate.css'], styles: [` .visible { opacity: 0 } .invisible { opacity: 0 } `], // define your animations animations: [ animation('myAnimation', [ // inline styles transition('state1 => state2', [ style({ opacity: 0 }), animate('1s', [ style({ opacity: 1 }), ]) ]), // component CSS classes transition('state2 => state3', [ style('.invisible'), animate('1s', [ style('.visible'), ]) ]), // component CSS keyframes transition('state4 => state5', [ animate(1000, style('@fadeIn')) ]), // programmatic keyframes transition('state5 => state6', [ animate('1000ms ease-out', [ style({ transform: 'scale(0)', offset: 0 }), style({ transform: 'scale(1.5) rotate(100deg)', offset: .33 }), style({ transform: 'scale(0.5) rotate(-80deg)', offset: .66 }), style({ transform: 'scale(1) rotate(0deg)', offset: 1 }) ]) ]) // auto-computed styles transition('state6 => state7', [ style({ height: 0 }), animate('1000ms ease-out', [ style({ height: '*' }) ]) ]) ]) ] })

2

Automatic Styling

Any CSS property can be used

Just use a * value

this would be very hard normally

@Component({ animations: [ animation('active', [ state('closed', style({ height: 0 })), state('open', style({ height: '*' })), transition('closed => open', [ animate('350ms ease-out') ]), transition('open => closed', [ animate('350ms ease-out') ]) ]) ] })

But where’s the JS code?

It's best to just use styles

Or create your own driver

But you can always return a promise

// apply dynamic properties animate('1000ms', [ style('.modal-styles').transform('clickXY') ]) // registered elsewhere function clickXY(element, styles, triggerEvent) { styles.top = triggerEvent.clientY; styles.left = triggerEvent.clientX; return styles; }
// apply dynamic properties animate('1000ms', [ style({ 'top': '[randomValue()]' }) ]) class MyCmp { randomValue() { return Math.random() } }

query, grouping, easing...

we can select individual elements

group them together

3

@Component({ style({ opacity: 0, transform: 'scale(0)' }), query('.section', [ style({ opacity: 0, transform: 'scale(0.8)' }) ]), // animate multiple elements in parallel group([ // animate the container animate('800ms 0ms cubic-bezier(0.68,-0.55,0.265,1.55)', [ style({ opacity: 1, transform: 'scale(1)' }) ]), // fetch inner items query('.section', [ style({ opacity: 0, transform:'scale(0.8)' }), animate('300ms 100ms ease-out', [ style({ opacity: 1, transform:'scale(1.2)' }) ]) animate({ transform:'scale(1)' }, '300ms ease-out') ]) ]) })

Staggering

full support

linear, reverse...

DOM specific

Create your own

// // Query and Stagger together... // query('[ngFor]', [ style({ opacity: 0 }) stagger(100, [ animate('1s', [ style({ opacity: }) ]) ]) ])
// apply dynamic properties query('[ngFor]', [ stagger('100ms customStagger', [ animate('1000ms', [ style('.some-class') ]) ]) ]) // registered elsewhere function customStagger(element, styles, triggerEvent, total, index) { return someDelayValue; }

4

Component Access

Components + Animations

components can access animations

for listening on status

or for programmatic control

class MyCmp { @AnimationControl('animationName') ani; ngOnInit() { this.ani.done.subscribe(() => { // when an animation finishes }) } }
class MyCmp { @AnimationControl('animationName') ani; setPosition(p) { this.ani.activeAnimation.setPosition(p); } }

5

transition('top => bottom', [ style({ 'background': 'red' }), animate(5000, [ style({ 'background': 'blue', 'transform':'scale(1)' }), style({ 'transform': 'scale(1.5)', 'top':'200px' }), style({ 'transform': 'rotate(125deg)', 'left':'400px' }), style({ 'top':'400px', 'left':'400px', 'background': 'red', 'transform': 'scale(1)' }) ]) ])

Custom Renderers

Non-DOM Platforms

We added an animate() method

More platforms can jump in

class MyRenderer implements Renderer { animate(element: any, startingStyles: AnimationStyles, keyframes: AnimationKeyframe[], duration: number, delay: number, easing: string): AnimationPlayer {} }

NativeScript!

When’s it coming out?

Thank you guys!

thank you

Matias Niemela

@yearofmoo

Robert Messerle

@Bobbo_O

yom.nu/ng-conf-2016-slides

github.com/matsko/ng-conf-demos