Angular Animation programmatically using Animation Builder

Da Feng
4 min readNov 1, 2021

Angular animation gives us a way to add animations to our application and make it more fun and straightforward to use. Typically we would start by adding the animations property inside the @component metadata, and define our animation states and styles there, so that our component has a defined animation.

Then we would need to bind @triggerName to the html element, so that the element will apply the defined animation. Based on the binded trigger change to different state, the animation for stateA to stateB will be triggered and applied.

<div [@openClose]=”isOpen ? ‘open’ : ‘closed’”>

Reuse animation

The above aproach is great in most of the cases. But sometimes, we need have more control of the animation. For example, how can we make our animation be more re-useable? I have a slide-in drawer, and a slide-in card but I don’t want to re-define and duplicate the same code of animation in both drawer component and card component. Is there a way to make the slide-in animation more re-useable and configurable?

In angular world, to make a piece of code re-useable, the first thing that comes into my mind is: directive. Right, that's what directive is supposed to do for us: extend functionality and behavior for element. But still, to share and re-use the animation, there are at least 2 issues:

  • How do we dynamically assign the functionality to the web element that needs the animation?
  • How to call and trigger the animation whenever we want?

directive easily solved the first issue for us. We can attach the directive to the component that requires animation, hence in the directive we have access to the element by injecting ElementRef. Now we only need to slove the second issue: how to trigger animation dynamically?

Directive with AminationBuilder

Luckily, we have AnimationBuilder, which is an injectable service that can produce an animation inside a component or directive, provided by BrowserAnimationModule.

To use it, we need

  • First, create our animationMetadata of type AnimationMetadata[], and call animationBuilder.build(animationMetadata), to create a factory instane of type AnimationFactory.
  • Second, use this instance and html element to create an animation player: factory.create(webElement)
  • Third, the player instance have many functions that we can call: play() to play animation, destroy() to destroy the instance.

Following is an example of slideAnimationDirective

Slide out animation doesn’t work when closing drawer.

Slide Out animation doesn’t work

A step back

Then I realized some problems. The enter animation works well, but the leave animation doesn’t work. That’s because the ngOnDestroy of directive happens after the host component’s ngOnDestroy. When we are trying to apply leave animation to host element, the element is already destroyed. Also the control of animation is actually very limited. Enter animation works because we took advantage of ngOnInit of directive and we know at that time we can play the entering animation, however besides that, it's actually difficult for us to know each state of animation and apply them dynamically.

So, attribute directive is actually not a good fit for re-useable animations in my opinion. Each animation could have different state, and we might want to call the animation at different time. But AnimationBuilder is definately usefull, so I'd take a step back: only make animation metadata re-useable, and put all of them into a constant file, and component that require animation will inject AnimationBuilder and import the animation metadata and play on the fly.

Final Example

With that in mind, here comes a use case, and we can try to implement a sample drawer: we have a button which will toggle drawer state (true / false), which will in turn add or remove drawer from DOM, and according animation should displayed.

app-component has the following html

<button (click)="toggleDrawer()"></button>
<app-drawer #drawer *ngIf="showDrawer"></app-drawer>

To implement the drawer with animation, first we will have all animations defined and saved in a constant file animations.ts

Now to make the drawer componet work well with the slide in and out animation, we can use AnimationPlayer to run animation whenever drawer state has been toggled. Note we can take advantage ofsetter , in which we are sure animation can be run before ngOnDestroy

Now the animation is working well

More from animation player

Animation player has many other useful functions, sometimes can be very useful also

AnimationPlayer/** Provides a callback to invoke when the animation finishes.*/onDone(fn: () => void): void;/** Provides a callback to invoke when the animation starts.*/onStart(fn: () => void): void;/** Provides a callback to invoke after the animation is destroyed.*/onDestroy(fn: () => void): void;* Reports whether the animation has started.*/hasStarted(): boolean;/** Restarts the paused animation.*/restart(): void;

--

--

Da Feng

I am a software developer, write blogs about Typescript, Angular, React, vue, Front-end technologies, also share some of my life stories and good memories here.