Animations are a key part of modern user experience. They add a layer of refinement to your interface and serve functional purposes. Due to their ubiquity, Angular developers should understand how to implement them properly.
This guide explores controlling Angular animations through code to enhance user experience and improve application quality.
Why Use Animations?
Businesses commonly rely on animations to steer users through their apps, making information easier to digest and clarifying straightforward workflows.
Hover and click effects are typical examples. While the cursor changing to a pointer helps, adding a hover effect to clickable items is an effective way to signal interactivity. Click animations, such as Angular Material’s ripple on buttons, provide immediate confirmation that the user's click was registered by the system.
Adding Animations to an Angular Project
Several methods exist for incorporating animations in Angular, including the framework's built-in animations module which requires no extra packages.
The standard practice involves listing animations in the `animations` array of the `Component` decorator. You then apply these to elements using animation directives (prefixed with `@`, e.g., `@slideIn` or `@fadeOut`). The following snippet demonstrates a typical setup for Angular Animations.
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { trigger, style, animate, transition } from '@angular/animations';
@Component({
standalone: true,
imports: [CommonModule],
selector: 'app-root'
template: `
<div class="card-container">
<div *ngIf="cardIsDisplayed" @fadeSlideInOut class="demo-card"></div>
</div>
`,
animations: [
trigger('fadeSlideInOut', [
transition(':enter', [
style({ opacity: 0, transform: 'translateY(10px)' }),
animate('500ms', style({ opacity: 1, transform: 'translateY(0)' })),
]),
transition(':leave', [
animate('500ms', style({ opacity: 0, transform: 'translateY(10px)' })),
]),
]),
],
})
export class AppComponent {
cardIsDisplayed = true;
toggleCard(): void {
this.cardIsDisplayed = !this.cardIsDisplayed;
}
}
This approach handles most scenarios and provides a straightforward way to bring animations into your application.
Modifying Angular Animations
The `AnimationBuilder` class allows you to create and control Angular Animations at runtime.
Beyond the core animation features, programmatically managed animations via the `AnimationPlayer` offer extra controls not available in the template-based method. These capabilities include:
- `pause` – halts the animation
- `finish` – stops the animation immediately
- `restart` – plays a **paused** animation again
- `destroy` – removes the animation
- `reset` – brings the animation back to its starting point
The `AnimationPlayer` also emits callbacks for crucial animation stages:
- `onStart` – fires at the start of the animation
- `onDone` – fires when the animation completes (either naturally or via the `finish` method)
- `onDestroy` – fires following the animation's destruction via the `destroy` method
If needed, you can also pass a callback to the `beforeDestroy` function to execute code before the animation gets destroyed.
Getting Started
Let's see how `AnimationBuilder` and `AnimationPlayer` operate in practice.
First, set up a target element along with some buttons to manage the animation.
```html
<!-- app.component.html –>
<div class="animation-demo-container">
<div class="card-container">
<div #demoCard class="demo-card"></div>
</div>
<div class="demo-controller-container">
<button class="demo-button" (click)="playAnimation()">Play</button>
<button class="demo-button" (click)="pauseAnimation()">Pause</button>
<button class="demo-button" (click)="stopAnimation()">Stop</button>
<button class="demo-button" (click)="resetAnimation()">Reset</button>
</div>
</div>
```
Then, apply some basic CSS to the component:
```scss
// app.component.scss
.animation-demo-container {
border-radius: 0.5rem;
border-width: 1px;
border-color: rgb(14 116 144);
background-color: rgb(30 41 59);
display: flex;
flex-direction: column;
min-height: 350px;
overflow: auto;
.card-container {
flex-grow: 1;
padding: 1.25rem;
.demo-card {
width: 10rem;
height: 10rem;
border-radius: 0.5rem;
background-color: rgb(125 211 252);
}
}
.demo-controller-container {
display: flex;
flex-direction: row;
padding: 1rem 1.25rem;
border-radius: 0 0 0.5rem 0.5rem;
gap: 0.75rem;
.demo-button {
background-color: rgb(255, 255, 255);
color: rgb(15 23 42);
border-radius: 0.25rem;
padding: 0.5rem 0.75rem;
font-weight: 500;
}
}
}
```
Now, for the animation logic. Begin by acquiring a reference to the target element using Angular's `ViewChild` decorator, passing the element's template reference variable (`#demoCard`) defined earlier.
```typescript
// app.component.ts
import { Component, ElementRef, ViewChild } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
standalone: true,
imports: [CommonModule],
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: [‘./app.component.scss’]
})
export class AppComponent {
@ViewChild('demoCard') demoCard: ElementRef | undefined;
}
```
Inject the `AnimationBuilder` service and define a private method that constructs the animation and returns the `AnimationPlayer` instance which will control it.
```typescript
// app.component.ts
import { Component, ElementRef, ViewChild, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { style, animate, AnimationBuilder, AnimationPlayer } from '@angular/animations';
@Component({
standalone: true,
imports: [CommonModule],
selector: 'app-root,
templateUrl: './app.component.html',
styleUrls: [‘./app.component.scss’]
})
export class AppComponent {
@ViewChild('demoCard') demoCard: ElementRef | undefined;
private animationBuilder = inject(AnimationBuilder);
private animationPlayer: AnimationPlayer | undefined;
private getAnimationPlayer(): AnimationPlayer | undefined {
if (!this.demoCard?.nativeElement) {
return;
}
if (!this.animationPlayer) {
const factory = this.animationBuilder.build(
[
style({ transform: 'rotate(0deg)' }),
animate('1000ms cubic-bezier(0.175, 0.885, 0.32, 1.275)', style({ transform: 'rotate(360deg)' }))
]
);
this.animationPlayer = factory.create(this.demoCard.nativeElement);
}
return this.animationPlayer;
}
}
```
Finally, implement the play, pause, stop, and reset handlers. Each one retrieves the player through `getAnimationPlayer()` and triggers the corresponding method on it.
```typescript
// app.component.ts
import { Component, ElementRef, ViewChild, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { style, animate, AnimationBuilder, AnimationPlayer } from '@angular/animations';
@Component({
standalone: true,
imports: [CommonModule],
selector: 'app-root,
templateUrl: './app.component.html',
styleUrls: [‘./app.component.scss’]
})
export class AppComponent {
@ViewChild('demoCard') demoCard: ElementRef | undefined;
private animationBuilder = inject(AnimationBuilder);
private animationPlayer: AnimationPlayer | undefined;
playAnimation(): void {
const player = this.getAnimationPlayer();
if (!player) {
return;
}
player.play();
}
pauseAnimation(): void {
const player = this.getAnimationPlayer();
if (!player || !player.hasStarted) {
return;
}
player.pause();
}
stopAnimation(): void {
const player = this.getAnimationPlayer();
if (!player || !player.hasStarted) {
return;
}
player.finish();
}
resetAnimation(): void {
const player = this.getAnimationPlayer();
if (!player || !player.hasStarted) {
return;
}
player.reset();
}
private getAnimationPlayer(): AnimationPlayer | undefined {
if (!this.demoCard?.nativeElement) {
return;
}
if (!this.animationPlayer) {
const factory = this.animationBuilder.build(
[
style({ transform: 'rotate(0deg)' }),
animate('1000ms cubic-bezier(0.175, 0.885, 0.32, 1.275)', style({ transform: 'rotate(360deg)' }))
]
);
this.animationPlayer = factory.create(this.demoCard.nativeElement);
}
return this.animationPlayer;
}
}
```
Following these steps should result in the animations shown here:

Check out Angular Animations Explorer for a live demonstration of this technique.
Summary
Angular Animations offer several implementation paths, each fitting different needs. For straightforward executions, the simpler template-based approach works well. If you need finer, more detailed control, the programmatic option with `AnimationBuilder` and `AnimationPlayer` is the way to go. Visit Angular Animations Explorer to explore other methods for adding animations.
Enjoyed this read or have feedback? Feel free to leave a comment or reach out on Twitter at @williamjuan27.
