SlideShare a Scribd company logo
1 of 41
Download to read offline
“I like to move it”
Anima&ng Angular Applica&ons
… but why?
‣ Expecta&on
‣ Con&nuity
‣ Narra&ve
‣ Rela&onship







https://medium.com/ux-in-motion/creating-usability-
with-motion-the-ux-in-motion-manifesto-a87a4584ddc
User Experience experts say:
@sulco
Tomek Sułkowski
engineer @ fourTheorem and @ StackBlitz trainer
declara3ve impera3ve
declara3ve impera3ve
basics
Setup
import {
BrowserAnimationsModule
} from '@angular/platform-browser/animations';
@NgModule({
imports: [
BrowserAnimationsModule,
]
})
export class AppModule { }
Just use BrowserAnimationsModule
Setup
import {
BrowserAnimationsModule
} from '@angular/platform-browser/animations';
@NgModule({
imports: [
BrowserAnimationsModule,
]
})
export class AppModule { }
Just use BrowserAnimationsModule
NoopAnimationsModule
for tests
Seman3cs
Trigger
func&on linking anima&on with a specific
element (i.e. an “anchor” for anima&on)


State
descrip&on of style in a discrete moment


Transi3on
descrip&on of the way styles change from
one state to another
Angular anima&ons nomenclature
state:
“on”
state:
“off”
trigger: @myFade
Implementa3on
trigger('myFade', []);
import { …stuff… } from ‘@angular/anima&ons’
state:
“on”
state:
“off”
trigger: @myFade
Implementa3on
trigger('myFade', [
state('on', style({opacity: 1.0})),
state('off', style({opacity: 0.3})),
]);
import { …stuff… } from ‘@angular/anima&ons’
state:
“on”
state:
“off”
trigger: @myFade
Implementa3on
trigger('myFade', [
state('on', style({opacity: 1.0})),
state('off', style({opacity: 0.3})),
transition('on !=> off', animate('300ms ease-in')),
transition('off !=> on', animate('100ms ease-in')),
]);
import { …stuff… } from ‘@angular/anima&ons’
state:
“on”
state:
“off”
trigger: @myFade
300ms
100ms
Implementa3on
trigger('myFade', [
state('on', style({opacity: 1.0})),
state('off', style({opacity: 0.3})),
transition('on !=> off’,
'off !=> on', animate('300ms ease-in')),
]);
import { …stuff… } from ‘@angular/anima&ons’
state:
“on”
state:
“off”
trigger: @myFade
300ms
300ms
Implementa3on
trigger('myFade', [
state('on', style({opacity: 1.0})),
state('off', style({opacity: 0.3})),
transition('on !!<=> off', animate('300ms ease-in’)),
]);
import { …stuff… } from ‘@angular/anima&ons’
state:
“on”
state:
“off”
trigger: @myFade
300ms
300ms
Implementa3on
trigger('myFade', [
state('on', style({opacity: 1.0})),
state('off', style({opacity: 0.3})),
transition(‘* !!<=> *’, animate('300ms ease-in’)),
]);
import { …stuff… } from ‘@angular/anima&ons’
state:
“on”
state:
“off”
trigger: @myFade
300ms
300ms
state:
“!!...”
300ms
Implementa3on
trigger('myFade', [
state('on', style({opacity: 1.0})),
state('off', style({opacity: 0.3})),
transition('void !=> *', animate('300ms')),
transition('* !=> void', animate('100ms')),
]);
transition(‘:enter', animate('300ms')),
transition(':leave', animate(‘100ms')),
import { …stuff… } from ‘@angular/anima&ons’
state:
“on”




void
trigger: @myFade




void
Usage
<div
[@myFade]="myState"
(click)="myState ""=== 'off' ? 'on' : 'off'"
>
Click me
"</div>
“@” - yet another symbol in Angular templates
@Component({
animations: [
trigger('myFade', [....])
]
})
state:
“on”
state:
“off”
trigger: @myFade
300ms
100ms
Usage
<div
[@myFade]="myState"
(click)="myState ""=== 'off' ? 'on' : 'off'"
>
Click me
!</div>
“@” - yet another symbol in Angular templates
@Component({
animations: [
trigger('myFade', [....])
]
})
state:
“on”
state:
“off”
trigger: @myFade
300ms
100ms
Usage
<div
[@myFade]="myState"
(click)="myState ""=== 'off' ? 'on' : 'off'"
(@myFade.start)="handleStart($event)"
(@myFade.done)="handleDone($event)"
>
Click me
!</div>
Callbacks (“let me know when you…”)
state:
“on”
state:
“off”
trigger: @myFade
300ms
100ms
Usage
<div
[@myFade]="myState"
(click)="myState ""=== 'off' ? 'on' : 'off'"
(@myFade.start)="handleStart($event)"
(@myFade.done)="handleDone($event)"
>
Click me
!</div>
Callbacks (“let me know when you…”)
state:
“on”
state:
“off”
trigger: @myFade
300ms
100ms
interface AnimationEvent {
fromState: string
toState: string
totalTime: number
phaseName: string
element: any
triggerName: string
disabled: boolean
}
staggering
Seman3cs 102
Query
select elements (inside a trigger)
that match a query token


Stagger
a series of anima&ons applied to subsequent
found element with a delay between them
Some more anima&on nomenclature <div>"</div>
trigger: @myStagger
<div>"</div>
<div>"</div>
:enter
.item
@*
<div>"</div>
<div>"</div>
<div>"</div>
examples of query tokens:
- anima&on keyword
- css selector
- trigger name
Implementa3on
<div [@myStagger]="items.length">
<div *ngFor="let item of items">
{{ item }}
"</div>
"</div>
<div>"</div>
trigger: @myStagger
<div>"</div>
<div>"</div>
<div>"</div>
<div>"</div>
<div>"</div>
Implementa3on
<div [@myStagger]="items.length">
<div *ngFor="let item of items">
{{ item }}
"</div>
"</div>
<div>"</div>
trigger: @myStagger
<div>"</div>
<div>"</div>
<div>"</div>
<div>"</div>
<div>"</div>
trigger('listAnimation', [
transition('* "=> *', [
query(':leave', [
stagger(100, [
animate('0.5s', style({ opacity: 0 }))
])
]),
query(':enter', [
style({ opacity: 0 }),
stagger(100, [
animate('0.5s', style({ opacity: 1 }))
])
])
])
])
Implementa3on
<div [@myStagger]="items.length">
<div *ngFor="let item of items">
{{ item }}
"</div>
"</div>
<div>"</div>
trigger: @myStagger
<div>"</div>
<div>"</div>
<div>"</div>
<div>"</div>
<div>"</div>
trigger('listAnimation', [
transition('* "=> *', [
query(':leave', [
stagger(100, [
animate('0.5s', style({ opacity: 0 }))
])
]),
query(':enter', [
style({ opacity: 0 }),
stagger(100, [
animate('0.5s', style({ opacity: 1 }))
])
])
])
])
router

anima3on
Implementa3on
<RouterOutlet>
is a direc&ve (exportAs: “outlet”) with
activatedRoute & activatedRouteData
proper&es
<main [@routerAnim]="getState(outletRef)">
<router-outlet #outletRef=“outlet”>
"</router-outlet>
"</main>
We can use this to get a state value
for an anima&on trigger.
getState(outletRef: RouterOutlet) {
return outletRef.activatedRouteData.name;
}
Implementa3on
trigger('routerTransition', [
transition('* ""<=> *', [
group([
query(':enter', [
style({transform: 'translateX(100%)'}),
animate('0.4s ease-in-out’,
style({transform: 'translateX(0%)'}))
], {optional: true}),
query(':leave', [
style({transform: 'translateX(0%)'}),
animate('0.4s ease-in-out’,
style({transform: ‘translateX(100%)'}))
], {optional: true}),
]),
]),
])
Heads up!
Anima&ons within the same route
won’t work by default.
@NgModule({
imports: [RouterModule.forRoot(routes)],
providers: [{
provide: RouteReuseStrategy,
useClass: CustomReuseStrategy
}],
})
export class CustomReuseStrategy implements RouteReuseStrategy {
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
return curr.component ""!== MyFruitComponent;
}
… (see the full class)
Heads up!
Anima&ons within the same route
won’t work by default.
@NgModule({
imports: [RouterModule.forRoot(routes)],
providers: [{
provide: RouteReuseStrategy,
useClass: CustomReuseStrategy
}],
})
export class CustomReuseStrategy implements RouteReuseStrategy {
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
return curr.component ""!== MyFruitComponent;
}
… (see the full class)
Heads up!
Anima&ons within the same route
won’t work by default.
@NgModule({
imports: [RouterModule.forRoot(routes)],
providers: [{
provide: RouteReuseStrategy,
useClass: CustomReuseStrategy
}],
})
export class CustomReuseStrategy implements RouteReuseStrategy {
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
return curr.component ""!== MyFruitComponent;
}
… (see the full class)
declara3ve impera3ve
Anima3onBuilder
Anima3onBuilder
with the build() method creates
a configured Anima&onFactory


Anima3onFactory
with the create() method creates
an Anima&onPlayer
Anima3onPlayer
allows to control the anima&on
like FormBuilder… but for anima&ons
Anima3onBuilder
const factory = this.animBuilder.build([
]);
this.player = factory.create(this.containerRef.nativeElement);
this.player.play();
this.player.onDone(() "=> resolve());
query(tilesQuery, style({ transform: 'translate3d(0, 0, 0)' }), { optional }),
query(
tilesQuery,
stagger(50, [
animate('900ms’,style({ transform: 'rotateX(180deg)' })),
]),
{ optional },
),
query(tilesQuery, style({ transform: 'translate3d(0, 0, 0)' }), { optional }),
query(
tilesQuery,
stagger(50, [
animate('900ms’,style({ transform: 'rotateX(180deg)' })),
]),
{ optional },
),
Anima3onBuilder
const factory = this.animBuilder.build([
]);
this.player = factory.create(this.containerRef.nativeElement);
this.player.play();
this.player.onDone(() "=> resolve());
Anima3onBuilder
const factory = this.animBuilder.build([
query(tilesQuery, style({ transform: 'translate3d(0, 0, 0)' }), { optional }),
query(
tilesQuery,
stagger(50, [
animate('900ms’,style({ transform: 'rotateX(180deg)' })),
]),
{ optional },
),
]);
this.player = factory.create(this.containerRef.nativeElement);
this.player.play();
this.player.onDone(() "=> resolve());
Actually…
Anima3onBuilder
const factory = this.animBuilder.build([
query(tilesQuery, style({ transform: 'translate3d(0, 0, 0)' }), { optional }),
query(
tilesQuery,
stagger(50, [
animate('900ms’,style({ transform: 'rotateX(180deg)' })),
]),
{ optional },
),
]);
this.player = factory.create(this.containerRef.nativeElement);
this.player.play();
this.player.onDone(() "=> resolve());
RxJS + Web Anima3ons API
const animationOptions = { duration: 900, fill: ‘forwards' }
const keyframes = [
{ transform: 'translate3d(0, 0, 0)' },
{ transform: 'rotateX(180deg)' }
];
return timer(0, 50, animationFrame)
.pipe(
take(tiles.length),
map(i "=> tiles[i].animate(keyframes, this.animationOptions)),
reduce((anims, anim) "=> [""...anims, anim], []),
delay(this.animationOptions.duration),
tap(anims "=> anims.forEach(anim "=> anim.cancel())),
);
teraz nie zapomnij pokazać dema :p
More on anima&ons
‣ Angular in Mo&on: 4 approaches to anima&on
‣ Angular Router Anima&ons - what they don't tell you
Ok, cheers!
h`ps://twi`er.com/sulco

More Related Content

What's hot

Jarv.us Showcase — SenchaCon 2011
Jarv.us Showcase — SenchaCon 2011Jarv.us Showcase — SenchaCon 2011
Jarv.us Showcase — SenchaCon 2011
Chris Alfano
 
Yearning jQuery
Yearning jQueryYearning jQuery
Yearning jQuery
Remy Sharp
 
Is HTML5 Ready? (workshop)
Is HTML5 Ready? (workshop)Is HTML5 Ready? (workshop)
Is HTML5 Ready? (workshop)
Remy Sharp
 
2010 bb dev con
2010 bb dev con 2010 bb dev con
2010 bb dev con
Eing Ong
 

What's hot (20)

Academy PRO: React native - building first scenes
Academy PRO: React native - building first scenesAcademy PRO: React native - building first scenes
Academy PRO: React native - building first scenes
 
Android Camera Architecture
Android Camera ArchitectureAndroid Camera Architecture
Android Camera Architecture
 
Dirty Durham: Dry cleaning solvents leaked into part of Trinity Park | News
Dirty Durham: Dry cleaning solvents leaked into part of Trinity Park | NewsDirty Durham: Dry cleaning solvents leaked into part of Trinity Park | News
Dirty Durham: Dry cleaning solvents leaked into part of Trinity Park | News
 
Enjoy the vue.js
Enjoy the vue.jsEnjoy the vue.js
Enjoy the vue.js
 
Modules and injector
Modules and injectorModules and injector
Modules and injector
 
Jarv.us Showcase — SenchaCon 2011
Jarv.us Showcase — SenchaCon 2011Jarv.us Showcase — SenchaCon 2011
Jarv.us Showcase — SenchaCon 2011
 
Universal JavaScript Web Applications with React - Luciano Mammino - Codemoti...
Universal JavaScript Web Applications with React - Luciano Mammino - Codemoti...Universal JavaScript Web Applications with React - Luciano Mammino - Codemoti...
Universal JavaScript Web Applications with React - Luciano Mammino - Codemoti...
 
Ui perfomance
Ui perfomanceUi perfomance
Ui perfomance
 
Best Practices for Magento Debugging
Best Practices for Magento Debugging Best Practices for Magento Debugging
Best Practices for Magento Debugging
 
Yearning jQuery
Yearning jQueryYearning jQuery
Yearning jQuery
 
Fixing Magento Core for Better Performance - Ivan Chepurnyi
Fixing Magento Core for Better Performance - Ivan ChepurnyiFixing Magento Core for Better Performance - Ivan Chepurnyi
Fixing Magento Core for Better Performance - Ivan Chepurnyi
 
How to create a camera2
How to create a camera2How to create a camera2
How to create a camera2
 
Is HTML5 Ready? (workshop)
Is HTML5 Ready? (workshop)Is HTML5 Ready? (workshop)
Is HTML5 Ready? (workshop)
 
Images and PWA in magento
Images and PWA in magentoImages and PWA in magento
Images and PWA in magento
 
AngularJS, More Than Directives !
AngularJS, More Than Directives !AngularJS, More Than Directives !
AngularJS, More Than Directives !
 
Responsive Web Design e a Ubiquidade da Web
Responsive Web Design e a Ubiquidade da WebResponsive Web Design e a Ubiquidade da Web
Responsive Web Design e a Ubiquidade da Web
 
An Event Apart Boston: Principles of Unobtrusive JavaScript
An Event Apart Boston: Principles of Unobtrusive JavaScriptAn Event Apart Boston: Principles of Unobtrusive JavaScript
An Event Apart Boston: Principles of Unobtrusive JavaScript
 
2010 bb dev con
2010 bb dev con 2010 bb dev con
2010 bb dev con
 
Mgd08 lab01
Mgd08 lab01Mgd08 lab01
Mgd08 lab01
 
JSLab. Алексей Волков. "React на практике"
JSLab. Алексей Волков. "React на практике"JSLab. Алексей Волков. "React на практике"
JSLab. Алексей Волков. "React на практике"
 

Similar to Animating angular applications

Private slideshow
Private slideshowPrivate slideshow
Private slideshow
sblackman
 
The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010
Fabien Potencier
 
Europython 2011 - Playing tasks with Django & Celery
Europython 2011 - Playing tasks with Django & CeleryEuropython 2011 - Playing tasks with Django & Celery
Europython 2011 - Playing tasks with Django & Celery
Mauro Rocco
 
Compose로 Android:Desktop 멀티플랫폼 만들기.pdf
Compose로 Android:Desktop 멀티플랫폼 만들기.pdfCompose로 Android:Desktop 멀티플랫폼 만들기.pdf
Compose로 Android:Desktop 멀티플랫폼 만들기.pdf
ssuserb6c2641
 

Similar to Animating angular applications (20)

Private slideshow
Private slideshowPrivate slideshow
Private slideshow
 
Clean Javascript
Clean JavascriptClean Javascript
Clean Javascript
 
The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010
 
Frontin like-a-backer
Frontin like-a-backerFrontin like-a-backer
Frontin like-a-backer
 
Europython 2011 - Playing tasks with Django & Celery
Europython 2011 - Playing tasks with Django & CeleryEuropython 2011 - Playing tasks with Django & Celery
Europython 2011 - Playing tasks with Django & Celery
 
Visual Component Testing -- w/ Gil Tayar (Applitools) and Gleb Bahmutov (Cyp...
Visual Component Testing  -- w/ Gil Tayar (Applitools) and Gleb Bahmutov (Cyp...Visual Component Testing  -- w/ Gil Tayar (Applitools) and Gleb Bahmutov (Cyp...
Visual Component Testing -- w/ Gil Tayar (Applitools) and Gleb Bahmutov (Cyp...
 
Nativescript angular
Nativescript angularNativescript angular
Nativescript angular
 
Mobile HTML, CSS, and JavaScript
Mobile HTML, CSS, and JavaScriptMobile HTML, CSS, and JavaScript
Mobile HTML, CSS, and JavaScript
 
Angular2: Quick overview with 2do app example
Angular2: Quick overview with 2do app exampleAngular2: Quick overview with 2do app example
Angular2: Quick overview with 2do app example
 
Bonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node jsBonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node js
 
A re introduction to webpack - reactfoo - mumbai
A re introduction to webpack - reactfoo - mumbaiA re introduction to webpack - reactfoo - mumbai
A re introduction to webpack - reactfoo - mumbai
 
Exploring Angular 2 - Episode 2
Exploring Angular 2 - Episode 2Exploring Angular 2 - Episode 2
Exploring Angular 2 - Episode 2
 
[22]Efficient and Testable MVVM pattern
[22]Efficient and Testable MVVM pattern[22]Efficient and Testable MVVM pattern
[22]Efficient and Testable MVVM pattern
 
준비하세요 Angular js 2.0
준비하세요 Angular js 2.0준비하세요 Angular js 2.0
준비하세요 Angular js 2.0
 
Jan 2017 - a web of applications (angular 2)
Jan 2017 - a web of applications (angular 2)Jan 2017 - a web of applications (angular 2)
Jan 2017 - a web of applications (angular 2)
 
8 things you didn't know about the Angular Router, you won't believe #6!
8 things you didn't know about the Angular Router, you won't believe #6!8 things you didn't know about the Angular Router, you won't believe #6!
8 things you didn't know about the Angular Router, you won't believe #6!
 
Svg, canvas e animations in angular (3 maggio 2019)
Svg, canvas e animations in angular (3 maggio 2019)Svg, canvas e animations in angular (3 maggio 2019)
Svg, canvas e animations in angular (3 maggio 2019)
 
Seven Peaks Speaks - Compose Screenshot Testing Made Easy
Seven Peaks Speaks - Compose Screenshot Testing Made EasySeven Peaks Speaks - Compose Screenshot Testing Made Easy
Seven Peaks Speaks - Compose Screenshot Testing Made Easy
 
Compose로 Android:Desktop 멀티플랫폼 만들기.pdf
Compose로 Android:Desktop 멀티플랫폼 만들기.pdfCompose로 Android:Desktop 멀티플랫폼 만들기.pdf
Compose로 Android:Desktop 멀티플랫폼 만들기.pdf
 
Applets
AppletsApplets
Applets
 

Recently uploaded

%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
masabamasaba
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
Health
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
masabamasaba
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
masabamasaba
 
The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is inside
shinachiaurasa2
 

Recently uploaded (20)

8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students
 
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
 
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
 
What Goes Wrong with Language Definitions and How to Improve the Situation
What Goes Wrong with Language Definitions and How to Improve the SituationWhat Goes Wrong with Language Definitions and How to Improve the Situation
What Goes Wrong with Language Definitions and How to Improve the Situation
 
Announcing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareAnnouncing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK Software
 
%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
 
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
 
AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation Template
 
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park %in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
 
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
 
The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is inside
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
 
%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare
 
WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?
 

Animating angular applications

  • 1. “I like to move it” Anima&ng Angular Applica&ons
  • 2. … but why? ‣ Expecta&on ‣ Con&nuity ‣ Narra&ve ‣ Rela&onship
 
 
 
 https://medium.com/ux-in-motion/creating-usability- with-motion-the-ux-in-motion-manifesto-a87a4584ddc User Experience experts say:
  • 3. @sulco Tomek Sułkowski engineer @ fourTheorem and @ StackBlitz trainer
  • 7. Setup import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; @NgModule({ imports: [ BrowserAnimationsModule, ] }) export class AppModule { } Just use BrowserAnimationsModule
  • 8. Setup import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; @NgModule({ imports: [ BrowserAnimationsModule, ] }) export class AppModule { } Just use BrowserAnimationsModule NoopAnimationsModule for tests
  • 9. Seman3cs Trigger func&on linking anima&on with a specific element (i.e. an “anchor” for anima&on) 
 State descrip&on of style in a discrete moment 
 Transi3on descrip&on of the way styles change from one state to another Angular anima&ons nomenclature state: “on” state: “off” trigger: @myFade
  • 10. Implementa3on trigger('myFade', []); import { …stuff… } from ‘@angular/anima&ons’ state: “on” state: “off” trigger: @myFade
  • 11. Implementa3on trigger('myFade', [ state('on', style({opacity: 1.0})), state('off', style({opacity: 0.3})), ]); import { …stuff… } from ‘@angular/anima&ons’ state: “on” state: “off” trigger: @myFade
  • 12. Implementa3on trigger('myFade', [ state('on', style({opacity: 1.0})), state('off', style({opacity: 0.3})), transition('on !=> off', animate('300ms ease-in')), transition('off !=> on', animate('100ms ease-in')), ]); import { …stuff… } from ‘@angular/anima&ons’ state: “on” state: “off” trigger: @myFade 300ms 100ms
  • 13. Implementa3on trigger('myFade', [ state('on', style({opacity: 1.0})), state('off', style({opacity: 0.3})), transition('on !=> off’, 'off !=> on', animate('300ms ease-in')), ]); import { …stuff… } from ‘@angular/anima&ons’ state: “on” state: “off” trigger: @myFade 300ms 300ms
  • 14. Implementa3on trigger('myFade', [ state('on', style({opacity: 1.0})), state('off', style({opacity: 0.3})), transition('on !!<=> off', animate('300ms ease-in’)), ]); import { …stuff… } from ‘@angular/anima&ons’ state: “on” state: “off” trigger: @myFade 300ms 300ms
  • 15. Implementa3on trigger('myFade', [ state('on', style({opacity: 1.0})), state('off', style({opacity: 0.3})), transition(‘* !!<=> *’, animate('300ms ease-in’)), ]); import { …stuff… } from ‘@angular/anima&ons’ state: “on” state: “off” trigger: @myFade 300ms 300ms state: “!!...” 300ms
  • 16. Implementa3on trigger('myFade', [ state('on', style({opacity: 1.0})), state('off', style({opacity: 0.3})), transition('void !=> *', animate('300ms')), transition('* !=> void', animate('100ms')), ]); transition(‘:enter', animate('300ms')), transition(':leave', animate(‘100ms')), import { …stuff… } from ‘@angular/anima&ons’ state: “on” 
 
 void trigger: @myFade 
 
 void
  • 17. Usage <div [@myFade]="myState" (click)="myState ""=== 'off' ? 'on' : 'off'" > Click me "</div> “@” - yet another symbol in Angular templates @Component({ animations: [ trigger('myFade', [....]) ] }) state: “on” state: “off” trigger: @myFade 300ms 100ms
  • 18. Usage <div [@myFade]="myState" (click)="myState ""=== 'off' ? 'on' : 'off'" > Click me !</div> “@” - yet another symbol in Angular templates @Component({ animations: [ trigger('myFade', [....]) ] }) state: “on” state: “off” trigger: @myFade 300ms 100ms
  • 19. Usage <div [@myFade]="myState" (click)="myState ""=== 'off' ? 'on' : 'off'" (@myFade.start)="handleStart($event)" (@myFade.done)="handleDone($event)" > Click me !</div> Callbacks (“let me know when you…”) state: “on” state: “off” trigger: @myFade 300ms 100ms
  • 20. Usage <div [@myFade]="myState" (click)="myState ""=== 'off' ? 'on' : 'off'" (@myFade.start)="handleStart($event)" (@myFade.done)="handleDone($event)" > Click me !</div> Callbacks (“let me know when you…”) state: “on” state: “off” trigger: @myFade 300ms 100ms interface AnimationEvent { fromState: string toState: string totalTime: number phaseName: string element: any triggerName: string disabled: boolean }
  • 22. Seman3cs 102 Query select elements (inside a trigger) that match a query token 
 Stagger a series of anima&ons applied to subsequent found element with a delay between them Some more anima&on nomenclature <div>"</div> trigger: @myStagger <div>"</div> <div>"</div> :enter .item @* <div>"</div> <div>"</div> <div>"</div> examples of query tokens: - anima&on keyword - css selector - trigger name
  • 23. Implementa3on <div [@myStagger]="items.length"> <div *ngFor="let item of items"> {{ item }} "</div> "</div> <div>"</div> trigger: @myStagger <div>"</div> <div>"</div> <div>"</div> <div>"</div> <div>"</div>
  • 24. Implementa3on <div [@myStagger]="items.length"> <div *ngFor="let item of items"> {{ item }} "</div> "</div> <div>"</div> trigger: @myStagger <div>"</div> <div>"</div> <div>"</div> <div>"</div> <div>"</div> trigger('listAnimation', [ transition('* "=> *', [ query(':leave', [ stagger(100, [ animate('0.5s', style({ opacity: 0 })) ]) ]), query(':enter', [ style({ opacity: 0 }), stagger(100, [ animate('0.5s', style({ opacity: 1 })) ]) ]) ]) ])
  • 25. Implementa3on <div [@myStagger]="items.length"> <div *ngFor="let item of items"> {{ item }} "</div> "</div> <div>"</div> trigger: @myStagger <div>"</div> <div>"</div> <div>"</div> <div>"</div> <div>"</div> trigger('listAnimation', [ transition('* "=> *', [ query(':leave', [ stagger(100, [ animate('0.5s', style({ opacity: 0 })) ]) ]), query(':enter', [ style({ opacity: 0 }), stagger(100, [ animate('0.5s', style({ opacity: 1 })) ]) ]) ]) ])
  • 27. Implementa3on <RouterOutlet> is a direc&ve (exportAs: “outlet”) with activatedRoute & activatedRouteData proper&es <main [@routerAnim]="getState(outletRef)"> <router-outlet #outletRef=“outlet”> "</router-outlet> "</main> We can use this to get a state value for an anima&on trigger. getState(outletRef: RouterOutlet) { return outletRef.activatedRouteData.name; }
  • 28. Implementa3on trigger('routerTransition', [ transition('* ""<=> *', [ group([ query(':enter', [ style({transform: 'translateX(100%)'}), animate('0.4s ease-in-out’, style({transform: 'translateX(0%)'})) ], {optional: true}), query(':leave', [ style({transform: 'translateX(0%)'}), animate('0.4s ease-in-out’, style({transform: ‘translateX(100%)'})) ], {optional: true}), ]), ]), ])
  • 29. Heads up! Anima&ons within the same route won’t work by default. @NgModule({ imports: [RouterModule.forRoot(routes)], providers: [{ provide: RouteReuseStrategy, useClass: CustomReuseStrategy }], }) export class CustomReuseStrategy implements RouteReuseStrategy { shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean { return curr.component ""!== MyFruitComponent; } … (see the full class)
  • 30. Heads up! Anima&ons within the same route won’t work by default. @NgModule({ imports: [RouterModule.forRoot(routes)], providers: [{ provide: RouteReuseStrategy, useClass: CustomReuseStrategy }], }) export class CustomReuseStrategy implements RouteReuseStrategy { shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean { return curr.component ""!== MyFruitComponent; } … (see the full class)
  • 31. Heads up! Anima&ons within the same route won’t work by default. @NgModule({ imports: [RouterModule.forRoot(routes)], providers: [{ provide: RouteReuseStrategy, useClass: CustomReuseStrategy }], }) export class CustomReuseStrategy implements RouteReuseStrategy { shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean { return curr.component ""!== MyFruitComponent; } … (see the full class)
  • 33. Anima3onBuilder Anima3onBuilder with the build() method creates a configured Anima&onFactory 
 Anima3onFactory with the create() method creates an Anima&onPlayer Anima3onPlayer allows to control the anima&on like FormBuilder… but for anima&ons
  • 34. Anima3onBuilder const factory = this.animBuilder.build([ ]); this.player = factory.create(this.containerRef.nativeElement); this.player.play(); this.player.onDone(() "=> resolve()); query(tilesQuery, style({ transform: 'translate3d(0, 0, 0)' }), { optional }), query( tilesQuery, stagger(50, [ animate('900ms’,style({ transform: 'rotateX(180deg)' })), ]), { optional }, ),
  • 35. query(tilesQuery, style({ transform: 'translate3d(0, 0, 0)' }), { optional }), query( tilesQuery, stagger(50, [ animate('900ms’,style({ transform: 'rotateX(180deg)' })), ]), { optional }, ), Anima3onBuilder const factory = this.animBuilder.build([ ]); this.player = factory.create(this.containerRef.nativeElement); this.player.play(); this.player.onDone(() "=> resolve());
  • 36. Anima3onBuilder const factory = this.animBuilder.build([ query(tilesQuery, style({ transform: 'translate3d(0, 0, 0)' }), { optional }), query( tilesQuery, stagger(50, [ animate('900ms’,style({ transform: 'rotateX(180deg)' })), ]), { optional }, ), ]); this.player = factory.create(this.containerRef.nativeElement); this.player.play(); this.player.onDone(() "=> resolve());
  • 38. Anima3onBuilder const factory = this.animBuilder.build([ query(tilesQuery, style({ transform: 'translate3d(0, 0, 0)' }), { optional }), query( tilesQuery, stagger(50, [ animate('900ms’,style({ transform: 'rotateX(180deg)' })), ]), { optional }, ), ]); this.player = factory.create(this.containerRef.nativeElement); this.player.play(); this.player.onDone(() "=> resolve());
  • 39. RxJS + Web Anima3ons API const animationOptions = { duration: 900, fill: ‘forwards' } const keyframes = [ { transform: 'translate3d(0, 0, 0)' }, { transform: 'rotateX(180deg)' } ]; return timer(0, 50, animationFrame) .pipe( take(tiles.length), map(i "=> tiles[i].animate(keyframes, this.animationOptions)), reduce((anims, anim) "=> [""...anims, anim], []), delay(this.animationOptions.duration), tap(anims "=> anims.forEach(anim "=> anim.cancel())), );
  • 40. teraz nie zapomnij pokazać dema :p
  • 41. More on anima&ons ‣ Angular in Mo&on: 4 approaches to anima&on ‣ Angular Router Anima&ons - what they don't tell you Ok, cheers! h`ps://twi`er.com/sulco