> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/angular/angular/llms.txt
> Use this file to discover all available pages before exploring further.

# Animations

> Create smooth, performant animations in Angular using the powerful animation system

## Overview

Angular's animation system provides a powerful DSL (Domain Specific Language) for creating complex animations and transitions. Built on top of the Web Animations API, it offers declarative syntax for common animation patterns.

## Setup

Import the animations module to enable animation support.

### Standalone Application

```typescript theme={null}
import { bootstrapApplication } from '@angular/platform-browser';
import { provideAnimations } from '@angular/platform-browser/animations';
import { AppComponent } from './app/app.component';

bootstrapApplication(AppComponent, {
  providers: [
    provideAnimations()
  ]
});
```

### NgModule Application

```typescript theme={null}
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule } from '@angular/core';

@NgModule({
  imports: [
    BrowserAnimationsModule
  ]
})
export class AppModule {}
```

## Basic Animations

### Simple Trigger

Create basic animations using triggers and states.

```typescript theme={null}
import { Component } from '@angular/core';
import { 
  trigger, 
  state, 
  style, 
  transition, 
  animate 
} from '@angular/animations';

@Component({
  selector: 'app-toggle',
  template: `
    <div class="box" [@openClose]="isOpen ? 'open' : 'closed'">
      <p>This box animates!</p>
    </div>
    <button (click)="toggle()">Toggle</button>
  `,
  animations: [
    trigger('openClose', [
      state('open', style({
        height: '200px',
        opacity: 1,
        backgroundColor: '#4CAF50'
      })),
      state('closed', style({
        height: '100px',
        opacity: 0.8,
        backgroundColor: '#F44336'
      })),
      transition('open => closed', [
        animate('0.5s')
      ]),
      transition('closed => open', [
        animate('0.3s')
      ])
    ])
  ]
})
export class ToggleComponent {
  isOpen = false;

  toggle(): void {
    this.isOpen = !this.isOpen;
  }
}
```

### Wildcard States

Use wildcards for flexible state matching.

```typescript theme={null}
import { Component } from '@angular/core';
import { trigger, state, style, transition, animate } from '@angular/animations';

@Component({
  selector: 'app-status',
  template: `
    <div class="status" [@statusChange]="currentStatus">
      Status: {{ currentStatus }}
    </div>
    <button (click)="setStatus('active')">Active</button>
    <button (click)="setStatus('inactive')">Inactive</button>
    <button (click)="setStatus('pending')">Pending</button>
  `,
  animations: [
    trigger('statusChange', [
      state('active', style({ backgroundColor: '#4CAF50', color: 'white' })),
      state('inactive', style({ backgroundColor: '#F44336', color: 'white' })),
      state('pending', style({ backgroundColor: '#FF9800', color: 'white' })),
      
      // Transition from any state to any state
      transition('* => *', [
        animate('300ms ease-in-out')
      ])
    ])
  ]
})
export class StatusComponent {
  currentStatus = 'inactive';

  setStatus(status: string): void {
    this.currentStatus = status;
  }
}
```

## Enter and Leave Animations

Animate elements as they enter or leave the DOM.

```typescript theme={null}
import { Component } from '@angular/core';
import { 
  trigger, 
  transition, 
  style, 
  animate 
} from '@angular/animations';

@Component({
  selector: 'app-list',
  template: `
    <button (click)="addItem()">Add Item</button>
    <ul>
      <li 
        *ngFor="let item of items; trackBy: trackByFn"
        [@fadeInOut]
        (click)="removeItem(item.id)"
      >
        {{ item.name }}
      </li>
    </ul>
  `,
  animations: [
    trigger('fadeInOut', [
      transition(':enter', [
        style({ opacity: 0, transform: 'translateY(-20px)' }),
        animate('300ms ease-out', style({ 
          opacity: 1, 
          transform: 'translateY(0)' 
        }))
      ]),
      transition(':leave', [
        animate('300ms ease-in', style({ 
          opacity: 0, 
          transform: 'translateX(100%)' 
        }))
      ])
    ])
  ]
})
export class ListComponent {
  items = [
    { id: 1, name: 'Item 1' },
    { id: 2, name: 'Item 2' }
  ];
  private nextId = 3;

  addItem(): void {
    this.items.push({ 
      id: this.nextId++, 
      name: `Item ${this.nextId}` 
    });
  }

  removeItem(id: number): void {
    this.items = this.items.filter(item => item.id !== id);
  }

  trackByFn(index: number, item: any): number {
    return item.id;
  }
}
```

<Note>
  Use `:enter` and `:leave` aliases for the special `void => *` and `* => void` transitions.
</Note>

## Advanced Animations

### Keyframes

Create complex multi-step animations.

```typescript theme={null}
import { Component } from '@angular/core';
import { 
  trigger, 
  transition, 
  style, 
  animate, 
  keyframes 
} from '@angular/animations';

@Component({
  selector: 'app-bounce',
  template: `
    <div 
      class="ball" 
      [@bounce]="animationState"
      (click)="bounce()"
    >
      Click me!
    </div>
  `,
  animations: [
    trigger('bounce', [
      transition('* => bouncing', [
        animate('1s', keyframes([
          style({ transform: 'translateY(0)', offset: 0 }),
          style({ transform: 'translateY(-100px)', offset: 0.3 }),
          style({ transform: 'translateY(0)', offset: 0.5 }),
          style({ transform: 'translateY(-50px)', offset: 0.7 }),
          style({ transform: 'translateY(0)', offset: 0.85 }),
          style({ transform: 'translateY(-20px)', offset: 0.95 }),
          style({ transform: 'translateY(0)', offset: 1 })
        ]))
      ])
    ])
  ]
})
export class BounceComponent {
  animationState = 'idle';

  bounce(): void {
    this.animationState = 'bouncing';
    setTimeout(() => this.animationState = 'idle', 1000);
  }
}
```

### Parallel Animations

Run multiple animations simultaneously using `group()`.

```typescript theme={null}
import { Component } from '@angular/core';
import { 
  trigger, 
  transition, 
  style, 
  animate, 
  group 
} from '@angular/animations';

@Component({
  selector: 'app-card',
  template: `
    <div 
      class="card" 
      [@cardFlip]="flipped ? 'flipped' : 'default'"
      (click)="flip()"
    >
      <div class="front">Front</div>
      <div class="back">Back</div>
    </div>
  `,
  animations: [
    trigger('cardFlip', [
      state('default', style({
        transform: 'rotateY(0)'
      })),
      state('flipped', style({
        transform: 'rotateY(180deg)'
      })),
      transition('default => flipped', [
        group([
          animate('600ms', style({ transform: 'rotateY(180deg)' })),
          animate('300ms', style({ opacity: 0.5 }))
        ])
      ]),
      transition('flipped => default', [
        group([
          animate('600ms', style({ transform: 'rotateY(0)' })),
          animate('300ms', style({ opacity: 1 }))
        ])
      ])
    ])
  ]
})
export class CardComponent {
  flipped = false;

  flip(): void {
    this.flipped = !this.flipped;
  }
}
```

### Sequential Animations

Chain animations using `sequence()`.

```typescript theme={null}
import { Component } from '@angular/core';
import { 
  trigger, 
  transition, 
  style, 
  animate, 
  sequence 
} from '@angular/animations';

@Component({
  selector: 'app-notification',
  template: `
    <div 
      *ngIf="visible" 
      class="notification" 
      [@slideAndFade]
    >
      {{ message }}
    </div>
  `,
  animations: [
    trigger('slideAndFade', [
      transition(':enter', [
        sequence([
          style({ 
            opacity: 0, 
            transform: 'translateX(100%)' 
          }),
          animate('300ms ease-out', style({ 
            transform: 'translateX(0)' 
          })),
          animate('200ms', style({ 
            opacity: 1 
          }))
        ])
      ]),
      transition(':leave', [
        sequence([
          animate('200ms', style({ 
            opacity: 0 
          })),
          animate('300ms ease-in', style({ 
            transform: 'translateX(100%)' 
          }))
        ])
      ])
    ])
  ]
})
export class NotificationComponent {
  visible = false;
  message = '';

  show(message: string, duration = 3000): void {
    this.message = message;
    this.visible = true;
    setTimeout(() => this.visible = false, duration);
  }
}
```

## Stagger Animations

Animate list items with a stagger effect.

```typescript theme={null}
import { Component } from '@angular/core';
import { 
  trigger, 
  transition, 
  style, 
  animate, 
  query, 
  stagger 
} from '@angular/animations';

@Component({
  selector: 'app-stagger-list',
  template: `
    <button (click)="toggle()">Toggle List</button>
    <ul *ngIf="show" [@listAnimation]>
      <li *ngFor="let item of items">{{ item }}</li>
    </ul>
  `,
  animations: [
    trigger('listAnimation', [
      transition('* => *', [
        query(':enter', [
          style({ 
            opacity: 0, 
            transform: 'translateY(20px)' 
          }),
          stagger(100, [
            animate('300ms ease-out', style({ 
              opacity: 1, 
              transform: 'translateY(0)' 
            }))
          ])
        ], { optional: true })
      ])
    ])
  ]
})
export class StaggerListComponent {
  show = false;
  items = ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5'];

  toggle(): void {
    this.show = !this.show;
  }
}
```

## Route Animations

Animate transitions between routes.

```typescript theme={null}
// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './home.component';
import { AboutComponent } from './about.component';

const routes: Routes = [
  { path: '', component: HomeComponent, data: { animation: 'HomePage' } },
  { path: 'about', component: AboutComponent, data: { animation: 'AboutPage' } }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {}

// animations.ts
import { 
  trigger, 
  transition, 
  style, 
  query, 
  group, 
  animate 
} from '@angular/animations';

export const slideInAnimation = trigger('routeAnimations', [
  transition('HomePage <=> AboutPage', [
    style({ position: 'relative' }),
    query(':enter, :leave', [
      style({
        position: 'absolute',
        top: 0,
        left: 0,
        width: '100%'
      })
    ], { optional: true }),
    query(':enter', [
      style({ left: '-100%' })
    ], { optional: true }),
    query(':leave', [
      // Leave animation not needed
    ], { optional: true }),
    group([
      query(':leave', [
        animate('300ms ease-out', style({ left: '100%' }))
      ], { optional: true }),
      query(':enter', [
        animate('300ms ease-out', style({ left: '0%' }))
      ], { optional: true })
    ])
  ])
]);

// app.component.ts
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { slideInAnimation } from './animations';

@Component({
  selector: 'app-root',
  template: `
    <div [@routeAnimations]="getRouteAnimationData()">
      <router-outlet #outlet="outlet"></router-outlet>
    </div>
  `,
  animations: [slideInAnimation]
})
export class AppComponent {
  getRouteAnimationData() {
    return this.outlet?.activatedRouteData?.['animation'];
  }
}
```

## Animation Callbacks

Listen to animation events.

```typescript theme={null}
import { Component } from '@angular/core';
import { 
  trigger, 
  state, 
  style, 
  transition, 
  animate 
} from '@angular/animations';

@Component({
  selector: 'app-callback',
  template: `
    <div 
      [@fadeInOut]="visible ? 'visible' : 'hidden'"
      (@fadeInOut.start)="onAnimationStart($event)"
      (@fadeInOut.done)="onAnimationDone($event)"
    >
      Content
    </div>
    <button (click)="toggle()">Toggle</button>
  `,
  animations: [
    trigger('fadeInOut', [
      state('visible', style({ opacity: 1 })),
      state('hidden', style({ opacity: 0 })),
      transition('visible <=> hidden', animate('500ms'))
    ])
  ]
})
export class CallbackComponent {
  visible = true;

  toggle(): void {
    this.visible = !this.visible;
  }

  onAnimationStart(event: any): void {
    console.log('Animation started:', event);
  }

  onAnimationDone(event: any): void {
    console.log('Animation completed:', event);
  }
}
```

## Reusable Animations

Create reusable animation definitions.

```typescript theme={null}
// animations/fade.ts
import { animation, style, animate } from '@angular/animations';

export const fadeIn = animation([
  style({ opacity: 0 }),
  animate('{{ duration }}', style({ opacity: 1 }))
]);

export const fadeOut = animation([
  animate('{{ duration }}', style({ opacity: 0 }))
]);

// component.ts
import { Component } from '@angular/core';
import { 
  trigger, 
  transition, 
  useAnimation 
} from '@angular/animations';
import { fadeIn, fadeOut } from './animations/fade';

@Component({
  selector: 'app-reusable',
  template: `
    <div [@fade]="show" *ngIf="show">
      Reusable animation
    </div>
  `,
  animations: [
    trigger('fade', [
      transition(':enter', [
        useAnimation(fadeIn, {
          params: { duration: '300ms' }
        })
      ]),
      transition(':leave', [
        useAnimation(fadeOut, {
          params: { duration: '200ms' }
        })
      ])
    ])
  ]
})
export class ReusableComponent {
  show = true;
}
```

## Best Practices

<CardGroup cols={2}>
  <Card title="Use Hardware Acceleration" icon="microchip">
    Animate `transform` and `opacity` for better performance.
  </Card>

  <Card title="Provide Animation Metadata" icon="tag">
    Use the `optional` flag in queries for flexible animations.
  </Card>

  <Card title="Disable in Tests" icon="vial">
    Use `provideNoopAnimations()` in tests for faster execution.
  </Card>

  <Card title="Reuse Definitions" icon="recycle">
    Create reusable animations with `animation()` and `useAnimation()`.
  </Card>
</CardGroup>

<Warning>
  Avoid animating properties like `width`, `height`, or `left` directly as they trigger layout recalculation. Use `transform: scale()` and `transform: translate()` instead.
</Warning>

## Additional Resources

* [Angular Animation Guide](https://angular.dev/guide/animations)
* [Web Animations API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API)
* [Animation Examples](https://angular.dev/examples)
