Animations
Advanced
Angular
Elad Bezalel
Angular Animations Core Team
Angular & Web Technologies GDE
Why bother?
CSS Animations
Easiest & Fastest
CSS Transitions
Animating the visual change between element style states
button {
background: blue;
color: white;
padding: 50px;
font-size: 50px;
}
button:active {
background: green;
transition: background 300ms ease-in;
}
button {
background:blue;
color:white;
padding:50px;
font-size:50px;
/* animation css here */
transition:300ms ease-in;
}
button:active {
background:green;
}
button:hover {
background:orange;
}
CSS Keyframes
Reuse a visual change no matter the state
@keyframes rotate {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.loading-indicator:before {
animation:800ms rotate infinite ease;
}
@keyframes rotate {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.loading-indicator:before {
animation:800ms rotate infinite ease;
}
CSS Animations
Pros
- Benefits Cascading
- Consistent API
- Good for simple animations
.gallery-entry .picture {
transition:300ms ease-out;
}
.gallery-entry:hover .picture {
transform:scale(1.2);
}
.gallery-entry.active .picture {
transform:scale(1.3);
}
#gallery {
transition:500ms all;
}
@media only screen and (max-width: 600px) {
#gallery {
height:calc(200vh - 100px);
grid-template: repeat(2, 50fr);
}
}
CSS Animations
Cons
- Very difficult to sequence
- difficult to intercept and manipulate
- No dynamic values
- Runs on the CPU*
Javascript Animations
Tricky and complex
Why Javascript?
- More control than CSS Animations
- More intuitive than CSS Animations
- Build Complex Choreographies
GreenSock
GSAP
- More control than CSS Animations
- More intuitive than CSS Animations
- Animate Anything
- Faster than CSS3 animations and transitions
- Build Complex Choreographies
- Works with SVG
- Solve browser compatibility issues
- 20x faster than jQuery
- 7 million sites use GSAP
Pros
GSAP Example
export function zoomElement(element, onComplete) {
const { content, picture, title } = getElements(element);
return new TimelineLite()
.set(content, { ... })
.set(picture, { ... })
.set(title, { ... })
.add('start')
.to(content, 0.5, { ... }, 'start')
.to(picture, 0.3, { ... },'start')
.to(title, 0.3, { ... }, 'start')
.eventCallback('onComplete', onComplete );
}
Declarative Timelines
GSAP
- Not as popular as CSS3
- Costs money
- Less learning resources
Cons
Combine JS + CSS!
Angular Animations
Best of all solutions
Angular issues with GSAP
- Manipulates DOM elements directly
- Not Angular Native
- Not aware of Router and transitions
- Not aware of ngIf, ngFor, ngSwitch
Angular Animations
- More control than CSS Animations
- Using CSS transitions and keyframes
- Animation DSL
- Build Complex Choreographies
- Works with SVG
- Solve browser compatibility issues
- Works with enter & leave
Pros
Simple Animations
Style Animations
<div class="open-close-container"
[style.background]="isOpen ? 'yellow' : 'red'"
(click)="isOpen = !isOpen">
<span>The box is now {{ isOpen ? 'Open' : 'Closed' }}!</span>
</div>
.open-close-container {
transition: background .3s ease;
}
Class Animations
<div class="open-close-container"
[class.opened]="isOpen"
(click)="isOpen = !isOpen">
<span>The box is now {{ isOpen ? 'Open' : 'Closed' }}!</span>
</div>
.open-close-container {
transition: background .3s ease;
background: red;
}
.open-close-container.opened {
background: yellow
}
More complex animations
With the DSL
@Triggers
@Component({
animations: [
trigger('myAnimation', [
//...
])
]
})
<div [@myAnimation]="myValueExpression"></div>
States
trigger('myAnimation', [
state('open', style({
height: '200px'
})),
state('closed', style({
height: '0px'
}))
})
Transitions
trigger('myAnimation', [
// ...
transition('open => closed', [
animate('.5s ease')
])
})
A "simple" open-close animation
trigger('openClose', [
state('open', style({
height: '200px',
opacity: 1,
backgroundColor: 'white'
})),
state('closed', style({
height: '*',
color: 'white',
backgroundColor: '#3F51B5'
})),
transition('open => closed', [
animate('.5s ease')
]),
transition('closed => open', [
animate('.2s ease')
]),
])
Enter & Leave Animations
<div *ngIf="myExpression" [@myAnimation]="expression"></div>
trigger('myAnimation', [
state('void', style({
height: '0px',
opacity: 0,
})),
state('*', style({
height: '*',
opacity: '*',
})),
transition('open <=> closed', [
animate('.5s ease')
]),
])
Enter & Leave Animations
<div *ngIf="myExpression" @myAnimation></div>
trigger('myAnimation', [
transition(':enter', [
style({ transform: 'translateX(-50px)'}),
animate('300ms ease-out', style({ transform: 'translateX(0px)'}))
]),
transition(':leave', [
animate('300ms ease-out', style({ transform: 'translateX(50px)'}))
]),
])
Choreographing
trigger('myMultiElementAnimation', [
transition('* => start', [
query('.some-class', [
animate(/*...*/)
]),
query('img', stagger(50, [
animate(/*...*/)
]))
])
])
Routing Animations
<div class="router-container"
[@routerAnimation]="prepareRouteState(outlet)"
(@routerAnimation.start)="scrollToTop()">
<router-outlet #outlet="outlet"></router-outlet>
</div>
export const ROUTES = [
{path:'', component: HomePageComponent, data: { animation: 'home' }},
{path:'profile/:id', component: ProfilePageComponent, data: {animation: 'profile'}}
]
@Component({
//...
animations: [
trigger('routerAnimation', [
// this will fire when the home page changes to the profile page and vice versa
transition('home <=> profile', [
style({ position: 'relative' }),
// the new routed page is first hidden
query(':enter', [
style({position:'absolute', opacity:0, top:'-100%' })
]),
// then we animate away the old page
query(':leave', [
style({ position: 'absolute', top:0 }),
animateChild(),
style({ opacity:0 }),
]),
// then we animate back in the new page
query(':enter', [
style({opacity:1, top:'0%'}),
animateChild()
]),
])
])
]
})
class AppComponent {...}
Choreographing
Altogether
animate pipe
The future
@Elad_Bezalel
Thanks!
Advanced Angular Animations
By eladbezalel
Advanced Angular Animations
- 1,561