Angular-Animations Utility Library
Easy, Reusable Animation Utility library for Angular Apps.
Angular Animations utility library is a collection of reusable and parametrized animations build for Angular 15+ that can be used in a declarative manner. It implements all animations from animate.css (and more). Works both with AOT and JIT compilations.
Quick links
Demo | StackBlitz Demo | StackBlitz Base Template
Table of Contents
- Getting Started
- Installation
- Usage
- Available Animations and Parameters
- Running Demo App
- Authors
- License
Getting Started
Prerequisites
Make sure you import BrowserAnimationModule
in your angular app.
npm i @angular/animations@latest --save
Import BrowserAnimationsModule
from @angular/platform-browser/animations
in your root NgModule
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
@NgModule({
declarations: [
...
],
imports: [
...
BrowserAnimationsModule
],
})
export class AppModule { }
Installation
npm i angular-animations --save
Usage
on enter
/ on leave
Animations Animations on enter / on leave are triggered in a moment when element is added to or removed from the dom.
Basic example would be with *ngIf
template directive.
Import animation functions that you want to use and add them to animations
in a component decorator:
import { fadeInOnEnterAnimation, fadeOutOnLeaveAnimation } from 'angular-animations';
@Component({
selector: '...',
templateUrl: '...',
styleUrls: ['...'],
animations: [
fadeInOnEnterAnimation(),
fadeOutOnLeaveAnimation()
]
})
and use them in the template:
<div *ngIf="CONDITION" [@fadeInOnEnter] [@fadeOutOnLeave]></div>
Animations with state or triggered by state changes
These animations take as an input a boolean value. Some animations, like Attention Seekers are triggered depending on the direction
parameter; bidirectional (<=>
) will be triggered by any state change, unidirectional (=>
) will be triggered only when state changes from false to true.
All in
and out
animations are triggered by changes of state from false
to true
. Animations that preserve state, like collapse
or rotate
display default state when the value is false
and transition to end state when the value is true
import { collapseAnimation, rubberBandAnimation } from 'angular-animations';
@Component({
...
animations: [
rubberBandAnimation(),
collapseAnimation(),
]
})
<div [@rubberBand]="rubberState"></div>
<div [@collapse]="collapseState"></div>
Parametrized animations
All animations are open for customizations. All of them have parameters: duration
and delay
, and if it make sense for an animation, additional ones: translate
, degrees
or scale
.
Parameters can be used either in a component decorator or dynamically in a template.
In a decorator:
@Component({
...
animations: [
fadeInUpOnEnterAnimation({ anchor: 'enter', duration: 1000, delay: 100, translate: '30px' }),
bounceOutDownOnLeaveAnimation({ anchor: 'leave', duration: 500, delay: 200, translate: '40px' })
]
})
<div *ngIf="CONDITION" [@enter] [@leave]></div>
Animations like Attention Seekers can take a direction parameter (cannot be in template)
@Component({
...
animations: [
// triggers when STATE changes from false to true
rubberBandAnimation({anchor: 'rubber', direction: '=>', duration: 500})
]
})
<div [@rubber]="STATE"></div>
In a template (providing option for dynamic changes):
@Component({
...
animations: [
fadeInUpOnEnterAnimation({ anchor: 'enter'),
]
})
<div *ngIf="CONDITION" [@enter]="{ value: '', params: { duration: 300, delay: 0, translate: '30px' } }" [@leave]></div>
<div *ngIf="CONDITION" [@enter]="{ value: '', params: { duration: 300, delay: 100, translate: '40px } }" [@leave]></div>
With parameters in a template, we can for example achieve staggering animations:
<div *ngFor="let i of [1,2,3]" [@enter]="{ value: '', params: { delay: i * 100 } }"></div>
Animation Callbacks
Each animation supports start
and done
callbacks to setup hook methods that get called at the start or end of animations.
We can add callbacks with the syntax (@trigger.start)
or (@trigger.done)
, where trigger
is the name of the animation trigger/anchor being used.
<div [@fadeIn]="animationState" (@fadeIn.start)="animStart($event)" (@fadeIn.done)="animDone($event)"></div>
The callbacks receive an AnimationEvent
that contains the following properties: fromState
phaseName
("start" or "done"), toState
and totalTime
import { AnimationEvent } from '@angular/animations';
//...
animStart(event: AnimationEvent) {
console.log('Animation Started', event);
}
animDone(event: AnimationEvent) {
console.log('Animation Ended', event);
}
You can find more information about Animation callbacks in the Angular docs
Loop animation
You can achieve looped animation by using done
callback. Define a variable that triggers animation and toggle it when animation done callback is called:
<div [@bounce]="animState" (@bounce.done)="animDone()"></div>
and in the component:
animState = false;
animDone() {
this.animState = !this.animState
}
Example: simple infinite loop animation
Example: repeat animation N times after clicking the button
Example: repeat animation until certain event occurs
Chain animations
You can chain animations (e.g. wait for the first animation to finish before the second one starts) by using the done
callback.
Example: OnEnter/OnLeave chained animations
Available Animations and Parameters
All animations have duration
and delay
params.
Animation | Default Anchor | OnEnter/OnLeave | Additional Params | |
---|---|---|---|---|
Attention Seekers | ||||
bounce | [@bounce] |
[@bounceOnEnter] |
||
flash | [@flash] |
[@flashOnEnter] |
||
pulse | [@pulse] |
[@pulseOnEnter] |
scale (default: 1.05) |
|
rubberBand | [@rubberBand] |
[@rubberBandOnEnter] |
||
shake | [@shake] |
[@shakeOnEnter] |
translate (default: '10px') |
|
swing | [@swing] |
[@swingOnEnter] |
||
tada | [@tada] |
[@tadaOnEnter] |
||
wobble | [@wobble] |
[@wobbleOnEnter] |
||
jello | [@jello] |
[@jelloOnEnter] |
||
heartBeat | [@heartBeat] |
[@heartBeatOnEnter] |
scale (default: 1.3) |
|
headShake | [@headShake] |
[@headShakeOnEnter] |
||
Bouncing entrances | ||||
bounceIn | [@bounceIn] |
[@bounceInOnEnter] |
||
bounceInDown | [@bounceInDown] |
[@bounceInDownOnEnter] |
translate (default: '3000px') |
|
bounceInLeft | [@bounceInLeft] |
[@bounceInLeftOnEnter] |
translate (default: '3000px') |
|
bounceInRight | [@bounceInRight] |
[@bounceInRightOnEnter] |
translate (default: '3000px') |
|
bounceInUp | [@bounceInUp] |
[@bounceInUpOnEnter] |
translate (default: '3000px') |
|
Bouncing exits | ||||
bounceOut | [@bounceOut] |
[@bounceOutOnLeave] |
||
bounceOutDown | [@bounceOutDown] |
[@bounceOutDownOnLeave] |
translate (default: '2000px') |
|
bounceOutLeft | [@bounceOutLeft] |
[@bounceOutLeftOnLeave] |
translate (default: '2000px') |
|
bounceOutRight | [@bounceOutRight] |
[@bounceOutRightOnLeave] |
translate (default: '2000px') |
|
bounceOutUp | [@bounceOutUp] |
[@bounceOutUpOnLeave] |
translate (default: '2000px') |
|
Fading entrances | ||||
fadeIn | [@fadeIn] |
[@fadeInOnEnter] |
||
fadeInDown | [@fadeInDown] |
[@fadeInDownOnEnter] |
translate (default: '100%') |
|
fadeInDownBig | [@fadeInDownBig] |
[@fadeInDownBigOnEnter] |
translate (default: '2000px') |
|
fadeInLeft | [@fadeInLeft] |
[@fadeInLeftOnEnter] |
translate (default: '100%') |
|
fadeInLeftBig | [@fadeInLeftBig] |
[@fadeInLeftBigOnEnter] |
translate (default: '2000px') |
|
fadeInRight | [@fadeInRight] |
[@fadeInRightOnEnter] |
translate (default: '100%') |
|
fadeInRightBig | [@fadeInRightBig] |
[@fadeInRightBigOnEnter] |
translate (default: '2000px') |
|
fadeInUp | [@fadeInUp] |
[@fadeInUpOnEnter] |
translate (default: '100%') |
|
fadeInUpBig | [@fadeInUpBig] |
[@fadeInUpBigOnEnter] |
translate (default: '2000px') |
|
Fading exits | ||||
fadeOut | [@fadeOut] |
[@fadeOutOnLeave] |
||
fadeOutDown | [@fadeOutDown] |
[@fadeOutDownOnLeave] |
translate (default: '100%') |
|
fadeOutDownBig | [@fadeOutDownBig] |
[@fadeOutDownBigOnLeave] |
translate (default: '2000px') |
|
fadeOutLeft | [@fadeOutLeft] |
[@fadeOutLeftOnLeave] |
translate (default: '100%') |
|
fadeOutLeftBig | [@fadeOutLeftBig] |
[@fadeOutLeftBigOnLeave] |
translate (default: '2000px') |
|
fadeOutRight | [@fadeOutRight] |
[@fadeOutRightOnLeave] |
translate (default: '100%') |
|
fadeOutRightBig | [@fadeOutRightBig] |
[@fadeOutRightBigOnLeave] |
translate (default: '2000px') |
|
fadeOutUp | [@fadeOutUp] |
[@fadeOutUpOnLeave] |
translate (default: '100%') |
|
fadeOutUpBig | [@fadeOutUpBig] |
[@fadeOutUpBigOnLeave] |
translate (default: '2000px') |
|
Flippers | ||||
flip | [@flip] |
[@flipOnEnter] |
||
flipInX | [@flipInX] |
[@flipInXOnEnter] |
degrees (default: 90) |
|
flipInY | [@flipInY] |
[@flipInYOnEnter] |
degrees (default: 90) |
|
flipOutX | [@flipOutX] |
[@flipOutXOnLeave] |
degrees (default: 90) |
|
flipOutY | [@flipOutY] |
[@flipOutYOnLeave] |
degrees (default: 90) |
|
Light speed | ||||
lightSpeedIn | [@lightSpeedIn] |
[@lightSpeedInOnEnter] |
translate (default: '100%') |
|
lightSpeedOut | [@lightSpeedOut] |
[@lightSpeedOutOnLeave] |
translate (default: '100%') |
|
Rotating entrances | ||||
rotateIn | [@rotateIn] |
[@rotateInOnEnter] |
degrees (default: -200) |
|
rotateInDownLeft | [@rotateInDownLeft] |
[@rotateInDownLeftOnEnter] |
degrees (default: -45) |
|
rotateInDownRight | [@rotateInDownRight] |
[@rotateInDownRightOnEnter] |
degrees (default: 45) |
|
rotateInUpLeft | [@rotateInUpLeft] |
[@rotateInUpLeftOnEnter] |
degrees (default: 45) |
|
rotateInUpRight | [@rotateInUpRight] |
[@rotateInUpRightOnEnter] |
degrees (default: -90) |
|
Rotating exits | ||||
rotateOut | [@rotateOut] |
[@rotateOutOnLeave] |
degrees (default: 200) |
|
rotateOutDownLeft | [@rotateOutDownLeft] |
[@rotateOutDownLeftOnLeave] |
degrees (default: 45) |
|
rotateOutDownRight | [@rotateOutDownRight] |
[@rotateOutDownRightOnLeave] |
degrees (default: -45) |
|
rotateOutUpLeft | [@rotateOutUpLeft] |
[@rotateOutUpLeftOnLeave] |
degrees (default: -45) |
|
rotateOutUpRight | [@rotateOutUpRight] |
[@rotateOutUpRightOnLeave] |
degrees (default: -90) |
|
Sliding entrances | ||||
slideInUp | [@slideInUp] |
[@slideInUpOnEnter] |
translate (default: '100%') |
|
slideInDown | [@slideInDown] |
[@slideInDownOnEnter] |
translate (default: '100%') |
|
slideInLeft | [@slideInLeft] |
[@slideInLeftOnEnter] |
translate (default: '100%') |
|
slideInRight | [@slideInRight] |
[@slideInRightOnEnter] |
translate (default: '100%') |
|
Sliding exits | ||||
slideOutUp | [@slideOutUp] |
[@slideOutUpOnLeave] |
translate (default: '100%') |
|
slideOutDown | [@slideOutDown] |
[@slideOutDownOnLeave] |
translate (default: '100%') |
|
slideOutLeft | [@slideOutLeft] |
[@slideOutLeftOnLeave] |
translate (default: '100%') |
|
slideOutRight | [@slideOutRight] |
[@slideOutRightOnLeave] |
translate (default: '100%') |
|
Zooming entrances | ||||
zoomIn | [@zoomIn] |
[@zoomInOnEnter] |
||
zoomInDown | [@zoomInDown] |
[@zoomInDownOnEnter] |
||
zoomInLeft | [@zoomInLeft] |
[@zoomInLeftOnEnter] |
||
zoomInRight | [@zoomInRight] |
[@zoomInRightOnEnter] |
||
zoomInUp | [@zoomInUp] |
[@zoomInUpOnEnter] |
||
Zooming exits | ||||
zoomOut | [@zoomOut] |
[@zoomOutOnLeave] |
||
zoomOutDown | [@zoomOutDown] |
[@zoomOutDownOnLeave] |
||
zoomOutLeft | [@zoomOutLeft] |
[@zoomOutLeftOnLeave] |
||
zoomOutRight | [@zoomOutRight] |
[@zoomOutRightOnLeave] |
||
zoomOutUp | [@zoomOutUp] |
[@zoomOutUpOnLeave] |
||
Specials | ||||
hinge | [@hinge] |
[@hingeOnLeave] |
||
jackInTheBox | [@jackInTheBox] |
[@jackInTheBoxOnEnter] |
||
rollIn | [@rollIn] |
[@rollInOnEnter] |
degrees (default: -120), translate (default: '-100%') |
|
rollOut | [@rollOut] |
[@rollOutOnLeave] |
degrees (default: 120), translate (default: '100%') |
|
Other | ||||
collapse | [@collapse] [@collapseHorizontally] |
[@expandOnEnter] [@collapseOnLeave] [@fadeInExpandOnEnter] [@fadeOutCollapseOnLeave] [@expandRightOnEnter] [@collapseLeftOnLeave] [@fadeInExpandRightOnEnter] [@fadeOutCollapseLeftOnLeave] |
||
rotate | [@rotate] |
- |
degrees (default: 90) |
|
hueRotate | [@hueRotate] |
- |
Running Demo App
npm install
npm start
Authors
- Chris Filipowski - filipows
License
This project is licensed under the MIT License - see the LICENSE file for details