ngAnimate2 = Animations + Layouts
http:// yom.nu / ng2eu-2016
Matias Niemelä (@yearofmoo)
matias [at] yearofmoo [dot] com
ng-europe 2016
NG1 / NG2
Matias
yearofmoo.com
Demos & Slides
Link to Slides
yom.nu/ng2eu-2016
Code Examples
github.com/matsko/ng2eu-2016-code
ngAnimate = Animations + Layouts
Let's talk animations
Let's talk layouts
Animations
Layout-driven
Visual cues
CSS or web-animations
Angular defines animations
Layouts
Foundation of UI
The result of CSS + HTML
Angular manipulates layout
Angular Animation Basics
Animations in Angular 2
Defined inside @Component
DSL (Domain Specific Language)
Layout / Component Triggers
State-Based
NG2 Animations 101
@Component({
animations: [
trigger('myAnimation', [ /* ... */ ])
],
template: `<div [@myAnimation]="state">...</div>`
})
class Cmp {}
The Animation DSL
Functional Composition
Animations are Platform Agnostic
AoT-friendly
No CSS Transitions/Keyframes
CSS-based animations...
Are tricky to detect
Limited expressiveness
Hard to program
DOM dependent
Web-Animations API
Web-animations are native-level
Programmatic, performant
Browser Support
- Chrome & Firefox
- Safari (TP), Edge (hopefully soon)
Angular uses the web-animations.js polyfill
Web Animations 101
element.animate([
{ opacity: 0 },
{ opacity: 1 }
], 1000)
Web Animations are strict
No CSS detection
Start/End styles must be defined
Transforming animations is hard
One element at a time
The NG2 Animation DSL...
Makes use of web-animations
That makes animations flexible
Via framework essentials
Integrated into @Component
Sample: web-animation
element.animate([
{ opacity: 0 },
{ opacity: 1 }
], 1000)
Sample: NG2 Animation DSL
trigger('myAnimation', [
transition('* => *', [
style({ opacity: 0 })
animate(1000, { opacity: 1 })
])
])
Benefits of the DSL
Component-Centric
Declarative Syntax
Platform Independence
Blackbox Implementation Details
Testing & Performance
Animations are invoked...
<!-- 1. template bindings -->
<div @trigger></div>
<div [@trigger]="state"></div>
<!-- 2. host bindings -->
@Component(...)
class Cmp {
@HostBinding('@trigger')
public state;
}
Sample: List of stuff
@Component(...)
class MonthListCmp {
months = ['janvier', 'février', ...]
}
Sample: List of stuff
<div *ngFor="let month of months"
class="month"
@monthAnimation>
{{ month }}
</div>
Sample: List of stuff
// inside of @Component
animations: [
trigger('monthAnimation', [
transition(':enter', [
style({ opacity: 0, width: 0 }),
animate(500, style({ width: '20%', opacity: 1 }))
]),
transition(':leave', [
animate(500, style({ opacity: 0, width: 0 }))
])
])
]
1
Sample: Open Close Component
@Component({ ... })
class OpenCloseCmp {
open = false; // used within template
toggle() { this.open = !this.open; }
}
Sample: Template Bindings
<!-- component-tpl.html -->
<button (click)="toggle()">
Open / Close
</button>
<div [@slideOpen]="open ? 'open' : 'closed'">
this container is animated!
</div>
Sample: Animation Definition
@Component({
templateUrl: 'component-tpl.html',
animations: [
trigger('slideOpen', [
state('open', style({ height: '*' })),
state('closed', style({ height: 0 })),
transition('closed <=> open', [
animate('500ms ease-out')
])
])
]
})
2
(@animation.callback)
Capture the start/end of animations
(@animation.start) ="onStart($event )"
(@animation.done) ="onDone($event )"
Sample: Animation Callbacks
<div [@slideOpen]="open ? 'open' : 'closed'"
(@slideOpen.start)="slideStarted($event)"
(@slideOpen.done)="slideDone($event)">
this container is animated!
</div>
3
How it Works
Animation Pipeline
Prep : Parsing, Instruction Building
Run : Platform Animation Calls
Cleanup : Teardown and reset
Preparation Phase: Parsing
Animations + CSS are parsed
Errors are reported
DSL code is optimized/completed
AST (Abstract Syntax Tree) constructed
Preparation Phase: Compilation
The AST is turned into code
This means machine-JS code
Fast, optimizable, basic
AoT friendly!
Sample: Animation DSL
trigger('slideOpen', [
state('open', style({ height: '*' })),
state('closed', style({ height: 0 })),
transition('closed <=> open', [
animate('500ms ease-out')
])
])
Sample: AoT Animation Output
var App_slideOpen_states = {'*': {}};
function App_slideOpen_factory(view,element,queries,currentState,nextState) {
view.cancelActiveAnimation(element,'slideOpen',(nextState == 'void'));
var collectedStyles = {};
var player = null;
var totalTime = 0;
var defaultStateStyles = App_slideOpen_states['*'];
var startStateStyles = App_slideOpen_states[currentState];
if ((startStateStyles == null)) { (startStateStyles = defaultStateStyles); }
var endStateStyles = App_slideOpen_states[nextState];
if ((endStateStyles == null)) { (endStateStyles = defaultStateStyles); }
jit_renderStyles11(element,view.renderer,jit_clearStyles12(startStateStyles));
if ((player == null)) { (player = new jit_NoOpAnimationPlayer13()); }
player.onDone(function() {
jit_renderStyles11(element,view.renderer,jit_prepareFinalAnimationStyles14(startStateStyles,endStateStyles));
});
view.queueAnimation(element,'slideOpen',player,totalTime,currentState,nextState);
}
Web Workers
Machine code runs in thread
UI animation code is called
Angular does this automatically
Features available today
style(data)
// supported now
style({ opacity: 0 })
// very soon!
style('.invisible')
animate(time, style)
animate("1s", style(...))
animate("1s", keyframes([
style({ ... offset: 0 }),
style({ ... offset: 0.5 }),
style({ ... offset: 1 }),
])
Animation Auto Styles
transition('* > *', [
style({ height: 0 }),
animate("1s",
style({ height: '*' ))
])
animation timing
animate(1000, ...)
animate("1s", ...)
animate("1s 500ms", ...)
animate("1s 500ms ease-out", ...)
animate("1s 500ms cubic-bezier(.29,.55,.53,1.53)", ...)
sequence()
// [] = sequence()
transition('* > *', [
animate(...),
animate(...)
])
group()
transition('* > *', [
style({ height: 0 }),
group([
animate(500, style({ transform: 'rotate(360deg)' }))
animate("1s ease-out", style({ height: '*' }))
])
]))
Animation Cancel
Animation Cancel Support
Features coming soon
In the next weeks/months
query() + select()
CSS Parser Feature
Programmatic Player Access
Renderer/JS integration
Finding Elements...
Trigger elements can only be animated
What about animating inner children?
query()
Finds inner elements to animate
Within the component template
Mixes nicely into sequence/group
Sample: query()
transition(':enter', [
query('headerRef, footerRef', style({ opacity: 0 })),
group([
query('headerRef',
animate("1s", style({ opacity: 1 }))
query('footerRef', [
animate("1s 500ms", style({ opacity: 1 }))
])
])
Sample: query html
<section *ngIf="...">
<header #headerRef>
...
</header>
<footer #footerRef>
...
</footer>
</section>
Sample: select()
transition(':enter', [
select('*', style({ opacity: 0 })),
group([
select('header',
animate("1s", style({ opacity: 1 }))
select('footer', [
animate("1s 500ms", style({ opacity: 1 }))
])
])
4
Styling CSS...
Inline styles are clunky
And not DRY
Very verbose
CSS Parser Feature
External stylesheets can be loaded
Classes and Keyframes can be referenced
Sample: CSS Referencing
@Component({
styleUrls: ['external.css'],
animations: [ /* ... */ ]
}
Sample: CSS Stylesheet
/* external.css stylesheet */
.invisible { opacity: 0; }
.visible { opacity: 1; }
@keyframes rotate {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
Sample: CSS Referencing
animations: [
trigger('active', [
transition('inactive > active', [
style('.invisible'),
animate('1s', [
style('.visible'),
keyframes('rotate')
])
])
])
]
Programmatic Player Access
Animations can be injected
Programmatic execution
Programmatic control
Player position access
Sample: Animation Player Access
class Cmp {
@Animation('move')
public moveAnimation;
startAnimation() {
var player = this.moveAnimation.start(this.elementRef);
player.setPosition(0..100);
}
}
5
Programmatic Evaluations
JS evaluation is missing
Style calculation / overriding?
External libraries?
External data?
The expr() method
Renderer-level code calls
Basically anything window-level
Animation data gets passed in
Sample: Animation Expressions
animation('rippleAtClick', [
style(expr('eventXY($event)'))
animate(1000, keyframes('ripple'))
])
Sample: Expression Definition
// animation.js (outside of angular)
window.eventXY = function(event) {
return {
top: event.client.x,
left: event.client.y
};
}
Sample: Expression Player
@import {AnimationPlayer} from "@angular/core";
class CustomAnimationPlayer
implements AnimationPlayer { /* ... */ }
window.customAnimation = element > new CustomAnimationPlayer(element);
Sample: Style Mixing
style([
'.red-color',
{ height: 100 },
expr('styleMePretty()'),
])
Layouts
Templates drive layout
HTML is the foundation
Angular updates HTML
Bindings change...
Structure changes...
Changes in layout are animated
Dimensional sizing
Structural removals
Decorative / Effects
What does CSS do?
Works with dimensional sizing
Highly dependent on layout type
block, inline, position:absolute?
forget about floats, flexbox and tables
6
What can Angular do?
Use state-based values for layout
Angular can handle the change
7
The end goal
Angular will will mix
Custom animations
Alongside automatic layout animations
And CSS-based effects
Links
Link to Slides
yom.nu/ng2eu-2016
Code Examples
github.com/matsko/ng2eu-2016-code