How to animate Router transitions in Angular

Update: after Angular v4.3 I wrote a new blogpost using some new features not available before. Check it out here

In this post I want to share a cool technique you can use to animate Router transitions using Angular. For the moment this is the official solution proposed by Matias Niemelä, the main contributor for Angular Animations.

Use the links below to see it applied:

Demo | Source | Variation

💣 Caution, this solution is experimental and only for learning purposes.

Find the latest Angular content following my feed at @gerardsans.

Demo Application

We will create a simple Application with two sections: Home and About. So we can animate the transitions between them. See the final result below.

See the Router transitions sliding to the left.

The Application will be composed by the top navigation and the main content. The top navigation will be shared between all sections (lines 4–7). See the layout for our Root Component below. We used the router-outlet element as a placeholder to tell the Router where we want to render our components when matched with a route (line 9).

For the top navigation, we used the routerLink directive passing static routes to create the different navigation urls (lines 5–6).

For dynamic routes you can use this alternative syntax:

<a [routerLink]="['users', 42]">John</a> // in the template <a href="#/users/42">John</a> // rendered

We used routerLinkActive directive to set up the active class to style the current section. So when we navigate to Home, it will render:

<a href="#/home" class="active">Home</a>

Setting up routes

In Angular the Router tries to match a route definition with the current url following the same order used during setup.

The main two routes tell the Router to instantiate the Home and About components (lines 3–4) when navigation matches their respective paths.

We also used two special routes for common behaviours. The route with empty path (line 2) covers the default route which will be empty unless we are navigating to a specific url. In that case, we indicated to redirect using the home route. The otherwise route (line 5) will catch any typos or undefined routes displaying a user-friendly 404 page.

For this demo we are using the hash location strategy (line 9) due to Plunker. If we had access to the backend, we could also use the path location strategy. This would require redirecting undefined routes to index.html to avoid 404 errors from the server.

Route Components

Let’s see the changes required to the Home Component below.

First we imported the routerTransition factory function (line 2). We used it in the animations property. This will create an animation trigger with the same name and apply the corresponding styles to the Component. Finally we added the routerTransition animation trigger binding to the host element using the empty string. This will allow us to keep the animation context when navigating to another component.

<router-outlet>

<home [@routerTransition]=""></home> // <-- host binding

</router-outlet>

The main idea for this solution is to link the navigation, adding the new component and removing the previous component, to trigger the :enter and :leave transitions. Let’s see the implementation details to see how it’s done.

Angular Animations

Angular is based on top of the Web Animations API, we use animation triggers to define a series of states and transitions between states. We also use styles that help us compose the desired effect using CSS properties.

For browsers that don’t support Web Animations API natively: IE/Edge, Safari, etc; you must include a polyfill.

Events that will trigger an animation:

attaching or dettaching an element to/from the view (corresponding to asterisk and void states)

and states) setting a state for a trigger. Eg: [@routerTransition]=”void”.

Our implementation uses a combination of the two.

Animation Implementation

Let’s see how we can use Angular Animations to achieve the desired effect. First of all, we used the routerTransition factory function to call slideToLeft (lines 3–5). Within this function we created the animation trigger composed by two states and two transitions (lines 8–19).

We defined two states: void and asterisk; and set their styles to achieve the desired sliding effect (line 9–10). By using {position: fixed} the components will be placed according to the viewport and slide through the page.

For the :enter transition we set an initial style positioning the component far right. Then we setup an animation to the final position setting an easing function and duration. For the :leave transition we moved the component far left using a similar approach.

We used transform instead of top, bottom, left, right for its better performance.

Example Navigation

Let’s see what happens when we navigate from Home to About. These two sequences will happen simultaneously:

the Home Component will be detached from the view by the Router. The styles associated with the void state are applied. This will trigger the :leave transition. The styles associated with the :leave transition will be applied gradually. The Home Component will be destroyed.

will be detached from the view by the Router. The styles associated with the state are applied. This will trigger the transition. The styles associated with the transition will be applied gradually. The Home Component will be destroyed. the About Component will be attached to the view by the Router. The About Component will be instantiated. The styles associated with the void state are applied. The ‘@routerTransition’ host binding will trigger the :enter transition. The styles associated with the :enter transition will be applied gradually. Finally the styles associated with asterisk will be applied.

Note that all styles either from defined states or transitions are being aggregated simultaneously.

Special Mention

The implementation shown in this article is mainly using a slightly modified version of this code part of the demos created by Matias Niemelä for ngEurope.

That’s all! Think I missed something? Contact me on @gerardsans or gerard.sans_at_gmail.com. Thanks for reading!