The animations used by a component are defined in the animations
section:
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
animations: [
trigger('divState', [
state('normal', style({
'background-color': 'red',
transform: 'translateX(0)'
})),
state('highlighted', style({
'background-color': 'blue',
transform: 'translateX(100px)'
})),
])
]
})
export class AppComponent {
state = 'normal'
// ...
}
Elements:
- trigger: is the animation definition. It required a name, that is used in the template, and a list of states.
- state: is one of the possible states of the animation. It has a name, used to identify the current, and a style definition associated.
In the template must be associated the name of the animation with the wanted state:
<!-- ... -->
<div
style="width: 100px; height: 100px;"
[@divState]="state"
>
</div>
<!-- ... -->
It is possible to change the state of the animation just assignign the new one:
@Component({
// ...
})
export class AppComponent {
// ...
onAnimate() {
this.state = this.state === 'normal' ? 'highlighted' : 'normal';
}
// ...
}
<!-- ... -->
<button class="btn btn-primary" (click)="onAnimate()">Animate!</button>
<!-- ... -->
In order to create a nice transition between two states it is possible to:
@Component({
animations: [
trigger('divState', [
state(
'normal',
// ...
),
state(
'highlighted',
// ...
),
transition('normal => highlighted', animate(300)),
transition('highlighted => normal', animate(600)),
]),
],
})
export class AppComponent {
// ...
}
The transition just need a starting and ending state as first argument,
and as second the first implementation can just be the time needed to be completed.
All the intermediate color and position changes are automatically calculated
by the framework.
If the timing of both transitions is the same, can used only one definition
with <=>
instead of =>
:
transition('normal <=> highlighted', animate(300)),
It is also possible to use the wild chart *
to select any possible state:
transition('shrunken <=> *', animate(500)),
It is possible to define intermediate transitions in this way:
transition('shrunken <=> *', [
style({
'background-color': 'orange',
}),
animate(
1000,
style({
borderRadius: '50px',
}),
),
animate(500),
]),
When an element is create or removed can be the state void
:
trigger('list1', [
state(
'in', // dummy name
style({
opacity: 1,
transform: 'translateX(0)',
}),
),
transition('void => *', [
style({
opacity: 0,
transform: 'translateX(-100px)',
}),
animate(300),
]),
transition('* => void', [
animate(
300,
style({
opacity: 0,
transform: 'translateX(100px)',
}),
),
]),
]),
in this case the current state is not interesting,
therefore is used the *
in the component and can be omitted
the state binding in the template:
<!-- ... -->
<ul class="list-group">
<li
class="list-group-item"
(click)="onDelete(item)"
[@list1]
*ngFor="let item of list">
{{ item }}
</li>
</ul>
<!-- ... -->
It is possible to better control all the intermediate states of
a transition with the KeyFrames
:
transition('void => *', [
animate(
1000,
keyframes([
style({
opacity: 0,
transform: 'translateX(-100px)',
offset: 0
}),
style({
opacity: 0.5,
transform: 'translateX(-50px)',
offset: 0.3
}),
style({
opacity: 1,
transform: 'translateX(-20px)',
offset: 0.8
}),
style({
opacity: 1,
transform: 'translateX(0px)',
offset: 1
}),
]),
),
]),
It is possible to perform two different animations on the same object
and at the same time using group
:
transition('* => void', [
group([
animate(
300,
style({
color: 'red'
}),
),
animate(
800,
style({
opacity: 0,
transform: 'translateX(100px)',
}),
),
]),
]),
It is possible to setup callbacks using:
<!-- ... -->
<div
style="width: 100px; height: 100px;"
[@divState]="state"
(@divState.start)="onAnimationStarted($event)"
(@divState.done)="onAnimationFinished($event)"
>
</div>
<!-- ... -->
@Component({
// ...
})
export class AppComponent {
// ...
onAnimationStarted(event: any) {
console.log(event);
}
onAnimationFinished(event: any) {
console.log(event);
}
}