Before we can talk about what Angular Animations are all about, it’s important to talk about why animations are important in the first place. Take a look at Material Design by Google. One of the core principles of the design language is that motion provides meaning. This is the idea that motion can help draw user’s focus and provide feedback.
This is nothing new; the idea has been around in the realm of traditional animation for decades. Motion is the mechanism used to convey intentions to a viewer through the actions of the character on screen. These movements allow us to understand what the animated object was going to do. Put simply, it creates the context for a particular action.
This concept can be applied to our apps, as we can use motion to create context and convert user interaction into awesome user experiences. Angular Animations is built on top of Web Animations API and runs natively on browsers that support it. If needed, it has the CSS keyframes fallback.
So, how do we get started creating these awesome user experiences? First, we need to import the Angular animations module. Essentially, Angular Animations is a DSL. By importing the BrowserAnimationsModule from @angular/platform-browser/animations, we can get started adding some cool animations in our app. After importing it, we configure it in our NgModule as shown below.
import { BrowserAnimationsModule } from '@angular/platform-browser/animations’; @NgModule({ declarations: [ AppComponent, ], imports: [ BrowserModule, BrowserAnimationsModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
Animations are linked using the `trigger` property. This trigger starts with a `@` and is placed on the component for animating. When a property is changed for a trigger or an element is taken on or off from the stage, the underlying animation is then triggered. In the example below, it will trigger the animation every time the length of the images change.
<section [@fade]="images.length" class="gallery-block compact-gallery"> <div class="container”> ... </div> </section>
The animation will start for the element with the ‘fade’ trigger when there is a value change of some kind. For a case with enter and leave, the `ngIf` can add or remove the element to the DOM, which will trigger the animation as well.
Now, we need to define the animation itself. This happens in the metadata of the component; we will add the animations property in the decorator. Animations are configured as a series of transitions, so within our animations array, we have organized our different states for the animation (i.e. fadeIn and fadeOut). We can define the styles which should be presented on those states inside of them. After the states, we define the transitions which describe how the styles change when we move between the defined states.
A `void` transition is one where the element is either added or removed from the DOM. `*` represents any state; this is useful if the elements are changing directions multiple times.
@Component({ ... animations: [ trigger('fade', [ transition(‘void => *', [ style({ opacity: ‘0' }), animate('300ms ease-in', style({ opacity: 1 })) ]), transition('* => void', animate('300ms ease-out', style({ opacity: 0 }))) ]) ] }) export class GalleryComponent { ... }
How do we define the style changes? We use the `style` and `animate` functions. These are essentially the functions which do all the work. If we use just the style function and pass in the object with css properties and values, it will apply the style immediately. If we pass in animate and style (or keyframes), then it will perform that over a period of time.
For example, to set a starting state with a fadeIn animation, your initial style will set `opacity: 0`. Then, we apply the animate function with the appropriate time when we are ready to transition and the style is set to `opacity: 1`. The code above shows an example where we are transitioning between the components moving into the screen and out of the screen again.
<section [@fade]="images.length” (@fade.start)=“triggerTangent($event)"> <div class="container”> ... </div> </section>
There will be times when we will want to get feedback on the different state the animation is in. For this, we have available animations callback. Using the syntax defined above, we can define these life cycle hooks like when the animation started and when it ended, so we can then perform any additional actions if required. Each callback emits an animation event, which has all the information about the animation like its name, time, fromState, toState, and even the phaseName which it is currently in.
This completes a high level architecture of the Angular Animations module. However, the animation modules go well beyond this and offer some more complex features as well. These include `query`, `group`, and `stagger`.
@Component({ ... animations: [ trigger('fade', [ transition(‘void => *', [ query(':enter', [ style({ opacity: '0' }), stagger(30, [ animate('300ms ease-in', style({ opacity: 1 })) ]) ]) ]), transition('* => void', animate('300ms ease-out', style({ opacity: 0 }))) ]) ] }) export class GalleryComponent { ... }
`Query` is used to target specific elements within the parent component and apply animations to them. The best part is that Angular handles setup, tear down, and clean up for these elements as they are being coordinated across the page. For example, I have a list of images in a gallery. If I want to target each individual element of the gallery, I can use the query to animate all of them. Or, as in the example above, I can target all the (child) elements that are in view.
`Stagger` is something you use with query. As the word suggests, it staggers multiple elements as their style changes; in other words, it spaces out the animation of each element to create a strong impact. This bundled with `group`, which allows you to group multiple animations together. ‘group’ allows users to create complex and beautiful animations in a few lines of code. The example above is animating a list into view; the ‘:enter’ query targets the elements which have just been created on the DOM and are coming into the canvas.
There are even more complex tools like `animateChlid` and `AnimationBuilder`, those only really come out when you really need to do some serious custom work. The basic idea behind the Angular Animations module is that it should be your de facto standard for animating your app, whether it’s a simple fade in animation or a complex orchestration of multiple elements on the whole page.