DEV Community

Ria Pacheco
Ria Pacheco

Posted on

Adding Animated Illustrations to an Angular App with Lottie (ngx-lottie)


Note: The concepts herein helped me better (and fully) understand Angular component re-usability through concepts like: data binding, component-in-component placement, @Input() decorators, and @Output() decorators. Hopefully it can help you in this same way too!


Problems

  • Since angular animations are meant for transitions / interactions, it's hard to get actual stop-motion graphics into the web app
  • Since the framework is css-driven (doesn't play nice with html5 in general, let alone html5's <canvas> element), you don't get to utilize cool new tools like hype4 (for the life of me the 'iframe' route never seems to work)
  • I'm not an After Effects professional, so the alternative of more abstracted tools is far more appealing.

Solution & Workflow Assumptions

Here's how I got animations from Lottie's free library into a re-usable angular component using the ngx-lottie package and angular's data / event binding features. Note:

  • This article assumes you have basic knowledge of JavaScript/TypeScript/Angular (with associated dependencies like Node installed)
  • For speed, the templates were created utilizing my @riapacheco/yutes package on npm (feel free to use it -- instructions for install is in npm docs)
  • The code from this demo is on my github, so feel free to fork it: https://github.com/riapacheco/lottie-demo


To learn more about Lottie, read more from their official site!


Skip ahead

Step 1: Add the File from Lottie
Step 2: Create the View Structure and General Style
Step 3: Install Lottie Packages
Step 4: Add the Lottie Animation to Splash Screen Component

Make it Reusable
Reusing in Another Component


Step 1: Add the File from Lottie

Create a new angular app by running the following command (--skip-tests added so that we don't generate tests files with our components):

$ ng new lottie-demo --skip-tests
Enter fullscreen mode Exit fullscreen mode

Head to LottieFiles.com and navigate to their Free Animations section.

Then, choose an animation and download it as the lottie JSON file ( saved this file as 'piggy-bank.json):
Image description

Create a folder called 'lottie' inside your 'src/assets' folder and add the file to it:

Image description


Step 2: Create the View Structure and General Style

Create a new component ($ ng g c shared/splash-screen) and replace all the content that's in the app.component.html file with the new components selector:

<!-- app.component.html -->
<app-splash-screen></app-splash-screen>
Enter fullscreen mode Exit fullscreen mode

Add the following structural scss to the splash-screen.component.SCSS file:

:host {  
  height: 100%;
  overflow: hidden;
  display: flex;
  flex-flow: column nowrap;
  align-items: center;
  justify-content: center;
  position: relative;
}
Enter fullscreen mode Exit fullscreen mode

In the component's template (HTML file), add the following code:

<div class="lottie-animation mt-10">  
  This is where the animation will go.
</div>

<div class="container-sm" style="text-align: center">  
  <div class="splash-text">
    <h1>
      Step 1
    </h1>

    Lorem ipsum dolor sit amet consectetur adipisicing elit. 
    Impedit eligendi obcaecati quisquam molestias animi dolores 
    vitae veniam ut provident repudiandae doloremque quae quo 
    vel, magnam labore eveniet natus fugiat. Aspernatur.  
  </div>

  <div class="button-div mt-3 mb-10">    
    <a class="btn btn-md accent light" style="margin: 1rem;">      
      Previous    
    </a>

    <a class="btn btn-md primary" style="margin: 1rem;">      
      Next    
    </a>  
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode


Note: all the styling classes here are simply classes used from my @riapacheco/yutes package so I wouldn't worry about them

This is how the view will render when you run ng serve:
Where the animation will go


Step 3: Install Lottie Packages

Install lottie with the following command (more details about this package can be found in their npm docs:

$ npm install lottie-web ngx-lottie
Enter fullscreen mode Exit fullscreen mode

Add the lottie module to your app.module.ts file with an exported function. (note: don't forget to add the CommonModule from @angular/common, so that you can use customized directives)

import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { BrowserModule } from '@angular/platform-browser';
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { SplashScreenComponent } from './shared/splash-screen/splash-screen.component';

// Add these two
import { LottieModule } from 'ngx-lottie';
import player from 'lottie-web';

// Export this function
export function playerFactory(): any {  
  return import('lottie-web');
}

@NgModule({  
  declarations: [    
    AppComponent,    
    SplashScreenComponent,    
    StepTwoComponent  
  ],  
  imports: [    
    BrowserModule,    
    AppRoutingModule,    
    CommonModule,
    // Add the module like so:    
    LottieModule.forRoot({ player: playerFactory }),  
  ],  
  providers: [],  
  bootstrap: [AppComponent]
})

export class AppModule { }
Enter fullscreen mode Exit fullscreen mode

Step 4: Add the Lottie Animation to Splash Screen Component

In the splash-screen.component.TS file, you have to add a variable that binds lottie's AnimationOptions (to reference the file path of your JSON file), and a new function that binds to the event (we'll see later in the template HTML file):

export class SplashScreenComponent implements OnInit {

  // This is the option that uses the package's AnimationOption interface  
  options: AnimationOptions = {    
    path: '/assets/lottie/piggy-bank.json'  
  };  

  constructor() { }  

  ngOnInit(): void {  }

  // This is the component function that binds to the animationCreated event from the package  
  onAnimate(animationItem: AnimationItem): void {    
    console.log(animationItem);  
  }
}
Enter fullscreen mode Exit fullscreen mode

Now in your template HTML file, you can replace the dummy text with the <ng-lottie> element and bind the options variable and the onAnimate event:

<!--splash-screen.component.html-->
<div class="lottie-animation mt-10">
  <ng-lottie
    [options]="options" 
    (animationCreated)="onAnimate($event)">
  </ng-lottie>
</div>

<!--...more code...-->
Enter fullscreen mode Exit fullscreen mode

Now when we run ng serve in our terminal, we'll see the animation appear in the browser:
Rendered in Browser


Make it Reusable

You can make this component re-usable with Angular's @Input() and @Outputs() so that parent components in the future can feed data or recognize events.

Prepare the component by adding @Input() to the options variable (making it available to other component) and by adding new variables to represent the text rendered in the component. If we want to communicate that a click event happened to the parent, we add @Output() to a variable that emits the event like so:

// splash-screen.component.ts
export class SplashScreenComponent implements OnInit {  

  @Input() options: AnimationOptions = {
    path: '/assets/lottie/piggy-bank.json'  
  };  
  @Input() titleText = 'Step 1';  
  @Input() stepParagraph = 'Lorem ipsum blah blah blah';  
  @Input() secondaryButtonText = 'Previous';  
  @Input() primaryButtonText = 'Next';  
  @Output() animationCreated = new EventEmitter();
  @Output() secondaryClick = new EventEmitter();  
  @Output() primaryClick = new EventEmitter();  

  constructor() { }  

  ngOnInit(): void {  }  

  onAnimate(animationItem: AnimationItem): void {   
    console.log(animationItem);    
    this.animationCreated.emit(animationItem);  
  }  

  onSecondaryClick(clickedSecondaryEvent): void {    
    this.secondaryClick.emit(clickedSecondaryEvent);
  }

  onPrimaryClick(clickedPrimaryEvent): void {    
    this.primaryClick.emit(clickedPrimaryEvent);  
  }
} 
Enter fullscreen mode Exit fullscreen mode

Then we just bind the data from the component to the template with either square brackets (on an element) or curly braces for actual string interpolation; or we bind events using parenthesis on elements:

<!--splash-screen.component.html-->
<div class="lottie-animation mt-10">
  <ng-lottie
    [options]="options" 
    (animationCreated)="onAnimate($event)">
  </ng-lottie>
</div>

<div
  class="container-sm"
  style="text-align: center">
  <div class="splash-text">
    <h1>      
      {{ titleText }}    
    </h1>    
    {{ stepParagraph }}  
  </div>
  <div
    class="button-div mt-3 mb-10">
    <a 
      class="btn btn-md accent light" 
      style="margin: 1rem;" 
      (click)="onSecondaryClick($event)">      
        {{ secondaryButtonText }}    
    </a>

    <a 
      class="btn btn-md primary" 
      style="margin: 1rem;" 
      (click)="onPrimaryClick($event)">      
        {{ primaryButtonText }}    
    </a>
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

This then renders like this (notice the new words from the component template):
Reusable component

Reusing in Another Component

To show how we re-use this component, add another free animation to the assets/lottie folder we created earlier. I've added one and named it chat.json.
chat.json in file

Create a new component called step-two by running the command $ ng g c views/step-two in your terminal


Note: For the sake of this demo we're pretending that we want to re-use the component by adding it to a newly generated component for every step of an onboarding process. This isn't advised (adds more work and lots more files than needed). Usually, you'd want to programmatically make it so variables change when items are clicked but stay within the same component. If you need help on how to do that just email me.

So that we can see the component in the browser, remove the contents of the app.component.html file (which is the splash-screen selector element) and replace it with the newly created step-two component's element:

<!--app.component.html-->
<app-step-two></app-step-two>
Enter fullscreen mode Exit fullscreen mode

Now you should see "step two works!" in your screen if you run ng serve which is the default text that comes with a component. Replace this text with the splash-screen selector element so that you instead see the original component we created:

<!--step-two.component.html-->
<app-splash-screen></app-splash-screen>
Enter fullscreen mode Exit fullscreen mode

To get access to the splash-screen component's inputs (without coming up errors at first) we'll create the variables inside the parent step-two component. The new 'stepTwoOptions' variable is now referencing the new 'chat.json' animation instead of the original. Notice that there aren't any input decorators or anything like that, since parent components shouldn't really know that a child component exists:

import { Component, OnInit } from '@angular/core';
import { AnimationOptions } from 'ngx-lottie';

@Component({  
  selector: 'app-step-two',  
  templateUrl: './step-two.component.html',  
  styleUrls: ['./step-two.component.scss']
})

export class StepTwoComponent implements OnInit { 
  stepTwoOptions: AnimationOptions = {    
    path: '/assets/lottie/chat.json'  
  };  
  stepTwoTitle = 'Step 2';  
  stepTwoParagraph = 'This is the second step of your stuff!';  

  constructor() { }  
  ngOnInit(): void {  }  

  clickNext(clickEvent): void {    
    console.log(clickEvent);  
  }
}
Enter fullscreen mode Exit fullscreen mode

So now, to link the variables together, we just need to bind the variables (like a ratio) to the splash-screen selector. In Angular, all square brackets represent data binding, and any parenthesis represent event binding:

<!-- step-two.component.html -->
<app-splash-screen
  [options]="stepTwoOptions"  
  [titleText]="stepTwoTitle"  
  [stepParagraph]="stepTwoParagraph"  
  [secondaryButtonText]="'Cancel'"  
  [primaryButtonText]="'Save'"  >
</app-splash-screen>
Enter fullscreen mode Exit fullscreen mode

Notice that the button texts don't actual bind to anything and I just added a string directly inside the template.

Together, this component renders like this:
Step 2 Render

Ria

Discussion (0)