Helps draw attention to elements
Makes applications feel more expressive and interactive
Helps draw attention to elements
Makes applications feel more expressive and interactive
Defined in CSS
Use AnimationEvent
on supported browsers
Have to manage a lot yourself
Builds on top of CSS animations
First class citizen as a component and router property
Hook in to the router to define animations consistently across your app
Bind directly in the template as well as supports HostBinding
in the component for more programmatic control
Angular manages coordination and supports complex animation sequences
Define different styles based on state
Define transitions between states
Define when animations trigger
Animations triggers emit callbacks so you can customize behavior
Angular handles setup, teardown, and cleanup as it coordinates elements across the page
Parent elements can explicitly control animation of child elements using animateChild
Apply multiple animation triggers on an element
query
to find elements matching a criteria
stagger
animations to fire with a timing gap between multiple elements
group
animations to run in parallel
sequence
animation steps
Define a set of animation styles using keyframes
Import the BrowserAnimationsModule
to your app
Add the animations
metadata property to the component
import { BrowserAnimationsModule } from
'@angular/platform-browser/animations';
@NgModule({
imports: [
BrowserAnimationsModule
],
declarations: [
AppComponent
],
bootstrap: [ AppComponent ]
})
export class AppModule { }
@Component({
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.css'],
animations: [
// animation triggers go here
]
})
Define styles for states
Trigger animation by binding to template
animations: [
trigger('message-state', [
state('even-messages', style({
backgroundColor: '#fce38a' // green
})),
state('odd-messages', style({
backgroundColor: '#95e1d3' // yellow
}))
])
]
<button
class="clear"
[@message-state]="messageService.messages.length % 2 ? 'even-messages' : 'odd-messages'"
(click)="messageService.clear()">clear
</button>
Declare the transition for the beginning state to the ending state
Looks something like this transition('begin => end', ...)
Define the animation using animate('duration delay easing', ...)
method
Start and ends with void
state
Match all states with wildcard state *
Built in shortcuts for :enter
and :leave
Built in helper for associating numeric values to transitions with :increment
and :decrement
Can declare multiple transition matches
Arrow can be bi-directional
Define animation
Trigger animation by binding to template
animations: [
trigger('animateIn', [
transition('void => *', [
style({transform: 'translateY(100%)'}),
animate('0.5s')
])
])
]
<ul class="heroes">
<li *ngFor="let hero of heroes" [@animateIn]>
<a routerLink="/detail/{{hero.id}}">
<span class="badge">{{hero.id}}</span> {{hero.name}}
</a>
<button class="delete" title="delete hero" (click)="delete(hero)">x</button>
</li>
</ul>
Stagger each element of the *ngFor
easily with built in animation methods.
Complex animations just got easier.
animations: [
trigger('animateIn', [
query(':enter', [
style({opacity: 0, transform: 'translateY(100%)'}),
stagger('0.5s', [
animate('0.5s', style({opacity: 1, transform: 'none'}))
])
], {optional: true})
])
]
Drive animation logic from component variables by defining state as a component property to conditionally animate.
<li *ngFor="let hero of heroes" [@animateIn]="animateState"></li>
animations: [
trigger('animateIn', [
transition('* => heroes-group', [
style({transform: 'translateY(100%)'}),
animate('0.5s')
]),
transition('* => heroes-stagger', [
query(':enter', [
style({opacity: 0, transform: 'translateY(100%)'}),
stagger('0.5s', [
animate('0.5s', style({opacity: 1, transform: 'none'}))
])
], {optional: true})
])
])
]
React to events using the built in Animation callbacks. Watch for animation started and completed events by using start
and done
properties.
<ul class="heroes" *ngIf="heroes" [@animateIn]
(@animateIn.done)="animationComplete($event)">
<li *ngFor="let hero of heroes">
<a routerLink="/detail/{{hero.id}}">
<span class="badge">{{hero.id}}</span> {{hero.name}}
</a>
</li>
</ul>
Hooks in to the router to have animation between views of a route change
Define animations for nested routes as well
Extra fancy
Add animation data to the Routes definition
Define the animation. This is where you might need to use the data in a transition
Add the template bindings
Update component with the router outlet to pull the animation data
const routes: Routes = [
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
{ path: 'dashboard', component: DashboardComponent, data: {routeAnimation: 'Dashboard'} },
{ path: 'detail/:id', component: HeroDetailComponent },
{ path: 'heroes', component: HeroesComponent, data: {routeAnimation: 'Heroes'} }
];
animations: [
trigger('routeAnimation', [
transition('Dashboard => Heroes', [
query(':enter, :leave', [], { optional: true })
]),
transition('Heroes => Dashboard', [
query(':enter, :leave', [], {optional: true})
])
])
]
<div [@routeAnimation]="prepareRoute(outlet)">
<router-outlet #outlet="outlet"></router-outlet>
</div>
public prepareRoute(outlet: RouterOutlet) {
return outlet && outlet.activatedRouteData && outlet.activatedRouteData.routeAnimation;
}
Helpful if you have more than two route states
Applicable to navigation lists
Use a numeric value to represent navigation progression
Utilize built in helpers :increment
and :decrement
to define transitions
const routes: Routes = [
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
{ path: 'dashboard', component: DashboardComponent, data: {routeAnimation: 1} },
{ path: 'detail/:id', component: HeroDetailComponent },
{ path: 'heroes-group', component: HeroesComponent, data: {routeAnimation: 2} }
{ path: 'heroes-stagger', component: HeroesComponent, data: {routeAnimation: 3} }
];
animations: [
trigger('routeAnimation', [
transition(':increment', [
query(':enter, :leave', [], { optional: true })
]),
transition(':decrement', [
query(':enter, :leave', [], {optional: true})
])
])
]
Animations can be defined separately from component
Reuse animations across multiple components
Keep code modular
Clean up component file
Define animation in separate file optionally using variables
Consume in component using useAnimation
method
export const myAnimation = animation([
query(':enter', style({ transform: '{{ enterParam }}'})),
query(':leave', style({ transform: '{{ leaveParam }}'}))
]);
animations: [
trigger('routeAnimation', [
transition(':increment', [
useAnimation(myAnimation, {
params: {
enterParam: 'translateX(-100%)',
leaveParam: 'translateX(200%)'
}
})
]),
transition(':decrement', [
useAnimation(myAnimation, {
params: {
enterParam: 'translateX(200%)',
leaveParam: 'translateX(-100%)'
}
})
])
])
]
Prevent animations from triggering while component DOM testing by importing the NoopAnimationsModule
in the TestBed
.
Happy testing!
Slides alisaduncan.github.io/angular-animations-2019
Animated Tour of Heroes App https://github.com/alisaduncan/angular-animations-code-2019
Animation docs on angular.io