GSAP Animations in Angular - Animation Directives
Tags: tutorial, angular, typescript, greensock
Published: 2020-06-17 14:53:31

# Create a directive for animation

As the second part of this tutorial we will create an **[attribute-directive](!https://angular.io/guide/attribute-directives)**, which will define a specific animation and its attributes.

We will start with a simple ***FadeIn*** animation.

But before starting with the real directive, we are going to create a generic class which will be extended by arbitrary directives, called **CoreAnimationDirective**.

> The goal of this directive is to define the standard methods and attributes for our future animation directives, to prevent a lot of repeats and hard-to-mantain directives.

### The ***CoreAnimationDirective*** class

Let's create! Before doing anything, let's create a specific folder for gsap directive.

From 'src/app' run in terminal `mkdir directives\gsap`.

> Note: use \ or / directory separator according to your system.

Your project should look like this:

![Project structure](https://dev-to-uploads.s3.amazonaws.com/i/3ads76wm0ld23hk7y6if.png)

Let's create the class, move to the gsap directory `cd directives\gsap & echo > core-animation.directive.ts`.

Open the file and write the following:

```typescript
import {ElementRef, EventEmitter, Input, Output} from '@angular/core';
import {TimelineMax} from 'gsap';

export class CoreAnimationDirective {
  @Input() duration = 1;
  @Input() delay = 0;

  @Output() complete: EventEmitter<null> = new EventEmitter();
  @Output() reverseComplete: EventEmitter<null> = new EventEmitter();
  protected timeline: TimelineMax;

  constructor(protected element: ElementRef) {}
}
```

A little bit of explanation:

* **@Input() duration** - the duration of the animation
* **@Input() delay** - the delay of the animation
* **@Output() complete** - event emitter for the animation complete callback
* **@Output() reverseComplete** - event emitter for the animation reverseComplete callback
* **protected timeline: TimelineMax** - the timeline for animation

Check the docs about *TimelineMax* and *eventCallbacks* [here](https://greensock.com/docs/v2/TimelineMax).

The ***constructor*** have only 1 param, the element (ElementRef), it's the ref to the native element which uses the directive.

We need to instantiate our ***timeline*** before animating it inside the constructor:

```typescript
constructor(protected element: ElementRef) {
  this.timeline = new TimelineMax({
    onComplete: _ => this.complete.emit(),
    onReverseComplete: _ => this.reverseComplete.emit(),
    paused:true,
    reversed:true
  });
}
```

This piece of code will create a new timeline, paused and not reversed, with a ***onComplete*** and ***onReverseComplete*** callback.

Let's create the ***animateIn*** method:

```typescript
protected animateIn() {
  if(this.timeline.isActive()) {
    this.timeline.kill();
  }
  this.timeline.play();
}
```

This method will kill the animation if in progress (you can return if is active so it will not stop), and play the current animation.

### The ***FadeInDirective*** class

Let's proceed with the next class, this class will perform the real animation.

It will extend the ***CoreAnimationClass***, as we don't need to repeat some portions of code.

Inside the *gsap* folder we've created previously, run `echo > fade-in-animation.directive.ts` and write the following code inside:

```typescript
import { Directive, OnInit, OnDestroy, ViewContainerRef, ElementRef, Input, EventEmitter } from '@angular/core';
import { TimelineMax } from 'gsap';
import { CoreAnimationDirective } from './core-animation.directive';

@Directive({
  selector: '[fadeInAnimation]'
})
export class FadeInAnimationDirective extends CoreAnimationDirective implements OnInit {
  constructor(protected element: ElementRef) {
    super(element);
  }

  ngOnInit() {
    // perform animation
    this.animateIn();
  }

  protected animateIn() {
    this.timeline.from(this.element.nativeElement, this.duration, {opacity:'0', ease:"Expo.easeInOut"}, this.delay);
    super.animateIn();
  }
}
```

This component extends the CoreAnimationDirective, so we don't need to define the @Input, @Output and other properties twice.

We need to create the opacity animation using **this.timeline.from** as follow:

`this.timeline.from(this.element.nativeElement, this.duration, {opacity:'0', ease:"Expo.easeInOut"}, this.delay);`

This will perform an animation on element opacity, from 0 to 1 (or to element opacity current value).