IMPORTANT: The Angular team recommends using native CSS for animations instead of the Animations package for all new code. Use this guide to understand existing code built with the Animations Package. SeeMigrating away from Angular's Animations package to learn how you can start using pure CSS animations in your apps.
Animation provides the illusion of motion: HTML elements change styling over time.Well-designed animations can make your application more fun and straightforward to use, but they aren't just cosmetic.Animations can improve your application and user experience in a number of ways:
Typically, animations involve multiple styletransformations over time.An HTML element can move, change color, grow or shrink, fade, or slide off the page.These changes can occur simultaneously or sequentially. You can control the timing of each transformation.
Angular's animation system is built on CSS functionality, which means you can animate any property that the browser considers animatable.This includes positions, sizes, transforms, colors, borders, and more.The W3C maintains a list of animatable properties on itsCSS Transitions page.
This guide covers the basic Angular animation features to get you started on adding Angular animations to your project.
The main Angular modules for animations are@angular/animations
and@angular/platform-browser
.
To get started with adding Angular animations to your project, import the animation-specific modules along with standard Angular functionality.
ImportprovideAnimationsAsync
from@angular/platform-browser/animations/async
and add it to the providers list in thebootstrapApplication
function call.
1bootstrapApplication(AppComponent, {2 providers: [3 provideAnimationsAsync(),4 ]5});
If you need to have an animation happen immediately when your application is loaded, you will want to switch to the eagerly loaded animations module. ImportprovideAnimations
from@angular/platform-browser/animations
instead, and useprovideAnimations
in place ofprovideAnimationsAsync
in thebootstrapApplication
function call.
ForNgModule
based applications importBrowserAnimationsModule
, which introduces the animation capabilities into your Angular root application module.
import {NgModule}from '@angular/core';import {BrowserModule}from '@angular/platform-browser';import {BrowserAnimationsModule}from '@angular/platform-browser/animations';@NgModule({ imports: [BrowserModule, BrowserAnimationsModule], declarations: [], bootstrap: [],})export class AppModule {}
If you plan to use specific animation functions in component files, import those functions from@angular/animations
.
import {Component, HostBinding, inject}from '@angular/core';import { trigger, state, style, animate, transition, // ...}from '@angular/animations';import {ChildrenOutletContexts, RouterLink, RouterOutlet}from '@angular/router';import {slideInAnimation}from './animations';@Component({ selector:'app-root', templateUrl:'app.component.html', styleUrls: ['app.component.css'], imports: [RouterLink, RouterOutlet], animations: [ slideInAnimation, // animation triggers go here ],})export class AppComponent { @HostBinding('@.disabled') public animationsDisabled = false; private contexts = inject(ChildrenOutletContexts); getRouteAnimationData() { return this.contexts.getContext('primary')?.route?.snapshot?.data?.['animation']; } toggleAnimations() { this.animationsDisabled= !this.animationsDisabled; }}
See allavailable animation functions at the end of this guide.
In the component file, add a metadata property calledanimations:
within the@Component()
decorator.You put the trigger that defines an animation within theanimations
metadata property.
import {Component, HostBinding, inject}from '@angular/core';import { trigger, state, style, animate, transition, // ...}from '@angular/animations';import {ChildrenOutletContexts, RouterLink, RouterOutlet}from '@angular/router';import {slideInAnimation}from './animations';@Component({ selector:'app-root', templateUrl:'app.component.html', styleUrls: ['app.component.css'], imports: [RouterLink, RouterOutlet], animations: [ slideInAnimation, // animation triggers go here ],})export class AppComponent { @HostBinding('@.disabled') public animationsDisabled = false; private contexts = inject(ChildrenOutletContexts); getRouteAnimationData() { return this.contexts.getContext('primary')?.route?.snapshot?.data?.['animation']; } toggleAnimations() { this.animationsDisabled= !this.animationsDisabled; }}
Let's animate a transition that changes a single HTML element from one state to another.For example, you can specify that a button displays eitherOpen orClosed based on the user's last action.When the button is in theopen
state, it's visible and yellow.When it's theclosed
state, it's translucent and blue.
In HTML, these attributes are set using ordinary CSS styles such as color and opacity.In Angular, use thestyle()
function to specify a set of CSS styles for use with animations.Collect a set of styles in an animation state, and give the state a name, such asopen
orclosed
.
HELPFUL: Let's create a newopen-close
component to animate with simple transitions.
Run the following command in terminal to generate the component:
ng g component open-close
This will create the component atsrc/app/open-close.component.ts
.
Use Angular'sstate()
function to define different states to call at the end of each transition.This function takes two arguments:A unique name likeopen
orclosed
and astyle()
function.
Use thestyle()
function to define a set of styles to associate with a given state name.You must usecamelCase for style attributes that contain dashes, such asbackgroundColor
or wrap them in quotes, such as'background-color'
.
Let's see how Angular'sstate()
function works with thestyle()
function to set CSS style attributes.In this code snippet, multiple style attributes are set at the same time for the state.In theopen
state, the button has a height of 200 pixels, an opacity of 1, and a yellow background color.
import {Component, input}from '@angular/core';import {trigger, transition, state, animate, style, AnimationEvent}from '@angular/animations';@Component({ selector:'app-open-close', animations: [ trigger('openClose', [ // ... state( 'open', style({ height:'200px', opacity:1, backgroundColor:'yellow', }), ), state( 'closed', style({ height:'100px', opacity:0.8, backgroundColor:'blue', }), ), transition('open => closed', [animate('1s')]), transition('closed => open', [animate('0.5s')]), transition('* => closed', [animate('1s')]), transition('* => open', [animate('0.5s')]), transition('open <=> closed', [animate('0.5s')]), transition('* => open', [animate('1s',style({opacity:'*'}))]), transition('* => *', [animate('1s')]), ]), ], templateUrl:'open-close.component.html', styleUrls: ['open-close.component.css'],})export class OpenCloseComponent { logging = input(false); isOpen = true; toggle() { this.isOpen= !this.isOpen; } onAnimationEvent(event: AnimationEvent) { if (!this.logging) { return; } // openClose is trigger name in this example console.warn(`Animation Trigger: ${event.triggerName}`); // phaseName is "start" or "done" console.warn(`Phase: ${event.phaseName}`); // in our example, totalTime is 1000 (number of milliseconds in a second) console.warn(`Total time: ${event.totalTime}`); // in our example, fromState is either "open" or "closed" console.warn(`From: ${event.fromState}`); // in our example, toState either "open" or "closed" console.warn(`To: ${event.toState}`); // the HTML element itself, the button in this case console.warn(`Element: ${event.element}`); }}
In the followingclosed
state, the button has a height of 100 pixels, an opacity of 0.8, and a background color of blue.
import {Component, input}from '@angular/core';import {trigger, transition, state, animate, style, AnimationEvent}from '@angular/animations';@Component({ selector:'app-open-close', animations: [ trigger('openClose', [ // ... state( 'open', style({ height:'200px', opacity:1, backgroundColor:'yellow', }), ), state( 'closed', style({ height:'100px', opacity:0.8, backgroundColor:'blue', }), ), transition('open => closed', [animate('1s')]), transition('closed => open', [animate('0.5s')]), transition('* => closed', [animate('1s')]), transition('* => open', [animate('0.5s')]), transition('open <=> closed', [animate('0.5s')]), transition('* => open', [animate('1s',style({opacity:'*'}))]), transition('* => *', [animate('1s')]), ]), ], templateUrl:'open-close.component.html', styleUrls: ['open-close.component.css'],})export class OpenCloseComponent { logging = input(false); isOpen = true; toggle() { this.isOpen= !this.isOpen; } onAnimationEvent(event: AnimationEvent) { if (!this.logging) { return; } // openClose is trigger name in this example console.warn(`Animation Trigger: ${event.triggerName}`); // phaseName is "start" or "done" console.warn(`Phase: ${event.phaseName}`); // in our example, totalTime is 1000 (number of milliseconds in a second) console.warn(`Total time: ${event.totalTime}`); // in our example, fromState is either "open" or "closed" console.warn(`From: ${event.fromState}`); // in our example, toState either "open" or "closed" console.warn(`To: ${event.toState}`); // the HTML element itself, the button in this case console.warn(`Element: ${event.element}`); }}
In Angular, you can set multiple styles without any animation.However, without further refinement, the button instantly transforms with no fade, no shrinkage, or other visible indicator that a change is occurring.
To make the change less abrupt, you need to define an animationtransition to specify the changes that occur between one state and another over a period of time.Thetransition()
function accepts two arguments:The first argument accepts an expression that defines the direction between two transition states, and the second argument accepts one or a series ofanimate()
steps.
Use theanimate()
function to define the length, delay, and easing of a transition, and to designate the style function for defining styles while transitions are taking place.Use theanimate()
function to define thekeyframes()
function for multi-step animations.These definitions are placed in the second argument of theanimate()
function.
Theanimate()
function (second argument of the transition function) accepts thetimings
andstyles
input parameters.
Thetimings
parameter takes either a number or a string defined in three parts.
animate (duration)
or
animate ('duration delay easing')
The first part,duration
, is required.The duration can be expressed in milliseconds as a number without quotes, or in seconds with quotes and a time specifier.For example, a duration of a tenth of a second can be expressed as follows:
As a plain number, in milliseconds:100
In a string, as milliseconds:'100ms'
In a string, as seconds:'0.1s'
The second argument,delay
, has the same syntax asduration
.For example:
'0.2s 100ms'
The third argument,easing
, controls how the animationaccelerates and decelerates during its runtime.For example,ease-in
causes the animation to begin slowly, and to pick up speed as it progresses.
Wait for 100ms, run for 200ms. Use a deceleration curve to start out fast and slowly decelerate to a resting point:'0.2s 100ms ease-out'
Run for 200ms, with no delay. Use a standard curve to start slow, accelerate in the middle, and then decelerate slowly at the end:'0.2s ease-in-out'
Start immediately, run for 200ms. Use an acceleration curve to start slow and end at full velocity:'0.2s ease-in'
HELPFUL: See the Material Design website's topic onNatural easing curves for general information on easing curves.
This example provides a state transition fromopen
toclosed
with a 1-second transition between states.
import {Component, input}from '@angular/core';import {trigger, transition, state, animate, style, AnimationEvent}from '@angular/animations';@Component({ selector:'app-open-close', animations: [ trigger('openClose', [ // ... state( 'open', style({ height:'200px', opacity:1, backgroundColor:'yellow', }), ), state( 'closed', style({ height:'100px', opacity:0.8, backgroundColor:'blue', }), ), transition('open => closed', [animate('1s')]), transition('closed => open', [animate('0.5s')]), transition('* => closed', [animate('1s')]), transition('* => open', [animate('0.5s')]), transition('open <=> closed', [animate('0.5s')]), transition('* => open', [animate('1s',style({opacity:'*'}))]), transition('* => *', [animate('1s')]), ]), ], templateUrl:'open-close.component.html', styleUrls: ['open-close.component.css'],})export class OpenCloseComponent { logging = input(false); isOpen = true; toggle() { this.isOpen= !this.isOpen; } onAnimationEvent(event: AnimationEvent) { if (!this.logging) { return; } // openClose is trigger name in this example console.warn(`Animation Trigger: ${event.triggerName}`); // phaseName is "start" or "done" console.warn(`Phase: ${event.phaseName}`); // in our example, totalTime is 1000 (number of milliseconds in a second) console.warn(`Total time: ${event.totalTime}`); // in our example, fromState is either "open" or "closed" console.warn(`From: ${event.fromState}`); // in our example, toState either "open" or "closed" console.warn(`To: ${event.toState}`); // the HTML element itself, the button in this case console.warn(`Element: ${event.element}`); }}
In the preceding code snippet, the=>
operator indicates unidirectional transitions, and<=>
is bidirectional.Within the transition,animate()
specifies how long the transition takes.In this case, the state change fromopen
toclosed
takes 1 second, expressed here as1s
.
This example adds a state transition from theclosed
state to theopen
state with a 0.5-second transition animation arc.
import {Component, input}from '@angular/core';import {trigger, transition, state, animate, style, AnimationEvent}from '@angular/animations';@Component({ selector:'app-open-close', animations: [ trigger('openClose', [ // ... state( 'open', style({ height:'200px', opacity:1, backgroundColor:'yellow', }), ), state( 'closed', style({ height:'100px', opacity:0.8, backgroundColor:'blue', }), ), transition('open => closed', [animate('1s')]), transition('closed => open', [animate('0.5s')]), transition('* => closed', [animate('1s')]), transition('* => open', [animate('0.5s')]), transition('open <=> closed', [animate('0.5s')]), transition('* => open', [animate('1s',style({opacity:'*'}))]), transition('* => *', [animate('1s')]), ]), ], templateUrl:'open-close.component.html', styleUrls: ['open-close.component.css'],})export class OpenCloseComponent { logging = input(false); isOpen = true; toggle() { this.isOpen= !this.isOpen; } onAnimationEvent(event: AnimationEvent) { if (!this.logging) { return; } // openClose is trigger name in this example console.warn(`Animation Trigger: ${event.triggerName}`); // phaseName is "start" or "done" console.warn(`Phase: ${event.phaseName}`); // in our example, totalTime is 1000 (number of milliseconds in a second) console.warn(`Total time: ${event.totalTime}`); // in our example, fromState is either "open" or "closed" console.warn(`From: ${event.fromState}`); // in our example, toState either "open" or "closed" console.warn(`To: ${event.toState}`); // the HTML element itself, the button in this case console.warn(`Element: ${event.element}`); }}
HELPFUL: Some additional notes on using styles withinstate
andtransition
functions.
state()
to define styles that are applied at the end of each transition, they persist after the animation completestransition()
to define intermediate styles, which create the illusion of motion during the animationtransition()
styles can be skipped, butstate()
styles can'ttransition()
argument:transition('on => off, off => void' )
An animation requires atrigger, so that it knows when to start.Thetrigger()
function collects the states and transitions, and gives the animation a name, so that you can attach it to the triggering element in the HTML template.
Thetrigger()
function describes the property name to watch for changes.When a change occurs, the trigger initiates the actions included in its definition.These actions can be transitions or other functions, as we'll see later on.
In this example, we'll name the triggeropenClose
, and attach it to thebutton
element.The trigger describes the open and closed states, and the timings for the two transitions.
HELPFUL: Within eachtrigger()
function call, an element can only be in one state at any given time.However, it's possible for multiple triggers to be active at once.
Animations are defined in the metadata of the component that controls the HTML element to be animated.Put the code that defines your animations under theanimations:
property within the@Component()
decorator.
import {Component, input}from '@angular/core';import {trigger, transition, state, animate, style, AnimationEvent}from '@angular/animations';@Component({ selector:'app-open-close', animations: [ trigger('openClose', [ // ... state( 'open', style({ height:'200px', opacity:1, backgroundColor:'yellow', }), ), state( 'closed', style({ height:'100px', opacity:0.8, backgroundColor:'blue', }), ), transition('open => closed', [animate('1s')]), transition('closed => open', [animate('0.5s')]), transition('* => closed', [animate('1s')]), transition('* => open', [animate('0.5s')]), transition('open <=> closed', [animate('0.5s')]), transition('* => open', [animate('1s',style({opacity:'*'}))]), transition('* => *', [animate('1s')]), ]), ], templateUrl:'open-close.component.html', styleUrls: ['open-close.component.css'],})export class OpenCloseComponent { logging = input(false); isOpen = true; toggle() { this.isOpen= !this.isOpen; } onAnimationEvent(event: AnimationEvent) { if (!this.logging) { return; } // openClose is trigger name in this example console.warn(`Animation Trigger: ${event.triggerName}`); // phaseName is "start" or "done" console.warn(`Phase: ${event.phaseName}`); // in our example, totalTime is 1000 (number of milliseconds in a second) console.warn(`Total time: ${event.totalTime}`); // in our example, fromState is either "open" or "closed" console.warn(`From: ${event.fromState}`); // in our example, toState either "open" or "closed" console.warn(`To: ${event.toState}`); // the HTML element itself, the button in this case console.warn(`Element: ${event.element}`); }}
When you've defined an animation trigger for a component, attach it to an element in that component's template by wrapping the trigger name in brackets and preceding it with an@
symbol.Then, you can bind the trigger to a template expression using standard Angular property binding syntax as shown below, wheretriggerName
is the name of the trigger, andexpression
evaluates to a defined animation state.
<div [@triggerName]="expression">…</div>;
The animation is executed or triggered when the expression value changes to a new state.
The following code snippet binds the trigger to the value of theisOpen
property.
<nav> <button type="button" (click)="toggle()">Toggle Open/Close</button></nav><div [@openClose]="isOpen ? 'open' : 'closed'" class="open-close-container"> <p>The box is now {{ isOpen? 'Open' : 'Closed' }}!</p></div>
In this example, when theisOpen
expression evaluates to a defined state ofopen
orclosed
, it notifies the triggeropenClose
of a state change.Then it's up to theopenClose
code to handle the state change and kick off a state change animation.
For elements entering or leaving a page (inserted or removed from the DOM), you can make the animations conditional.For example, use*ngIf
with the animation trigger in the HTML template.
HELPFUL: In the component file, set the trigger that defines the animations as the value of theanimations:
property in the@Component()
decorator.
In the HTML template file, use the trigger name to attach the defined animations to the HTML element to be animated.
Here are the code files discussed in the transition example.
import {Component, input}from '@angular/core';import {trigger, transition, state, animate, style, AnimationEvent}from '@angular/animations';@Component({ selector:'app-open-close', animations: [ trigger('openClose', [ // ... state( 'open', style({ height:'200px', opacity:1, backgroundColor:'yellow', }), ), state( 'closed', style({ height:'100px', opacity:0.8, backgroundColor:'blue', }), ), transition('open => closed', [animate('1s')]), transition('closed => open', [animate('0.5s')]), transition('* => closed', [animate('1s')]), transition('* => open', [animate('0.5s')]), transition('open <=> closed', [animate('0.5s')]), transition('* => open', [animate('1s',style({opacity:'*'}))]), transition('* => *', [animate('1s')]), ]), ], templateUrl:'open-close.component.html', styleUrls: ['open-close.component.css'],})export class OpenCloseComponent { logging = input(false); isOpen = true; toggle() { this.isOpen= !this.isOpen; } onAnimationEvent(event: AnimationEvent) { if (!this.logging) { return; } // openClose is trigger name in this example console.warn(`Animation Trigger: ${event.triggerName}`); // phaseName is "start" or "done" console.warn(`Phase: ${event.phaseName}`); // in our example, totalTime is 1000 (number of milliseconds in a second) console.warn(`Total time: ${event.totalTime}`); // in our example, fromState is either "open" or "closed" console.warn(`From: ${event.fromState}`); // in our example, toState either "open" or "closed" console.warn(`To: ${event.toState}`); // the HTML element itself, the button in this case console.warn(`Element: ${event.element}`); }}
<nav> <button type="button" (click)="toggle()">Toggle Open/Close</button></nav><div [@openClose]="isOpen ? 'open' : 'closed'" class="open-close-container"> <p>The box is now {{ isOpen? 'Open' : 'Closed' }}!</p></div>
:host { display:block; margin-top:1rem;}.open-close-container { border:1px solid #dddddd; margin-top:1em; padding:20px 20px 0px 20px; color:#000000; font-weight:bold; font-size:20px;}
You learned to add animation to a transition between two states, usingstyle()
andstate()
along withanimate()
for the timing.
Learn about more advanced features in Angular animations under the Animation section, beginning with advanced techniques intransition and triggers.
The functional API provided by the@angular/animations
module provides a domain-specific language (DSL) for creating and controlling animations in Angular applications.See theAPI reference for a complete listing and syntax details of the core functions and related data structures.
Function name | What it does |
---|---|
trigger() | Kicks off the animation and serves as a container for all other animation function calls. HTML template binds totriggerName . Use the first argument to declare a unique trigger name. Uses array syntax. |
style() | Defines one or more CSS styles to use in animations. Controls the visual appearance of HTML elements during animations. Uses object syntax. |
state() | Creates a named set of CSS styles that should be applied on successful transition to a given state. The state can then be referenced by name within other animation functions. |
animate() | Specifies the timing information for a transition. Optional values fordelay andeasing . Can containstyle() calls within. |
transition() | Defines the animation sequence between two named states. Uses array syntax. |
keyframes() | Allows a sequential change between styles within a specified time interval. Use withinanimate() . Can include multiplestyle() calls within eachkeyframe() . Uses array syntax. |
group() | Specifies a group of animation steps (inner animations) to be run in parallel. Animation continues only after all inner animation steps have completed. Used withinsequence() ortransition() . |
query() | Finds one or more inner HTML elements within the current element. |
sequence() | Specifies a list of animation steps that are run sequentially, one by one. |
stagger() | Staggers the starting time for animations for multiple elements. |
animation() | Produces a reusable animation that can be invoked from elsewhere. Used together withuseAnimation() . |
useAnimation() | Activates a reusable animation. Used withanimation() . |
animateChild() | Allows animations on child components to be run within the same timeframe as the parent. |
HELPFUL: Check out thispresentation, shown at the AngularConnect conference in November 2017, and the accompanyingsource code.
You might also be interested in the following: