It’s all about the little things

The little things in User Experience (UX) & User Interface (UI) Design are important. They are what set you apart from the rest. They create character and personality. They compliment well designed UI, and solidify the theme.

If you’re at the stage of development where you afford to focus on the more detailed UX through micro-interactions, providing your users with an extra boost of dopamine, whilst demonstrating mind-bending web dev skills (or just importing a library that does the mind-bending stuff for you), then implementing micro-interactions like these is what can leave your user with a lasting impression. It can be the reason they remember you through a sea of competition.

I remember when I first clapped a story on Medium and saw the confetti. It was a ridiculous thing to be that affected by. But I was like “how do I do that”.

Well I’m going to show you.

React happens to have a library for these micro-interactions, referred to as user ‘rewards’. It’s called react-rewards which supports a spray of confetti, emoji’s or memphis out-of-the-box. I thought this was it; that there wasn’t anything similar that I could easily use in Angular (I found this library searching for the cause of the clap confetti initially).

Angular doesn’t have a library for these little ‘rewards’. BUT the npm package canvas-confetti is where the magic originates. So we will install that package and use it for our confetti functionality, inside an Angular Component.

A GIF of the confetti that we will be cooking up and baking in our NgOven

Let’s celebrate with Angular

Installing canvas-confetti

First install the canvas-confetti package from node package manager.

$ npm install canvas-confetti

Create an Angular Component — CelebrateComponent

I’m going to create a component using Angular CLI, that’ll interact with an Angular Service and spray full-screen confetti in response to a celebrate event.

$ ng g c modules/shared/celebrate

This will also import the CelebrateComponent class into the SharedModule (module structure not important, just an example).

You’ll also note that I use ChangeDetectionStrategy.OnPush by default. I do this for performance reasons; to avoid triggering too many unnecessary change detection runs. Since you can let Angular know manually when to expect changes with this.changeDetectorRef.detectChanges(); (after injecting private changeDetectorRef: ChangeDetectorRef into the constructor).

Your CelebrateComponent class will start off like this.

starting celebrate.component.ts

Declare instance fields

We’ll want some instance fields to store the canvas element for re-use, plus the dynamically-imported canvas-confetti library, in addition to creating some observables to listen out for events.

In essence, we want to:

Listen to an RXJS Observable ( celebrate$ ) that will come from CelebrateService (that we will create), so we can programmatically trigger confetti in response to a user event.

( ) that will come from (that we will create), so we can programmatically trigger confetti in response to a user event. Be informed when the component is destroyed, providing this event notification through a Subject. We will make that Subject responsible for ensuring that there are no memory leaks in observable subscriptions for that component ( destroy ).

For this, the destroy Subject will be created. We can then call destroy.next() in the NgOnDestroy lifecycle. The observable of the subject ( destroy ) will be created ( destroy$ ), providing us a way to listen to the destroyed event.

Listen to the created destroy$ Observable, by providing it as the value for the RXJS operator takeUntil so that we unsubscribe from the celebrate$ observable eventually.

to the created Observable, by providing it as the value for the RXJS operator so that we unsubscribe from the observable eventually. Store the canvas-confetti library, in an instance variable confettiLib , so that we don’t import it twice. We will use an ES6 dynamic import, import('canvas-confetti') , subsequently storing the value.

This also ensures that we don’t have to import it unless it is used, as it will be rarely used, unless you use it with a button or similarly frequent event.

Store the confetti canvas function (confettiCanvas) so we can resuse it and only create it once.

Using an ES6 dynamic import allows Angular to recognise the library as its own chunk, reducing the main.js bundle size.

If you don’t support ES6 code in your application, you can just remove the code with the dynamic import and place import * as canvasConfetti from 'canvas-confetti' at the top of your component file.

set up celebrate.component.ts

Create CelebrateService

Before we go deeper into the CelebrateComponent, let’s create CelebrateService which will remain simple for this example.

We just need to provide a way to toggle the celebration from anywhere in the application in response to user behaviour. Some examples of this user behaviour could be:

On User Signup .

. Click Button (likes, share, create).

Button (likes, share, create). Completed Profile (if you provide a checklist that they complete).

Profile (if you provide a checklist that they complete). Tutorial/Demo/Walkthrough Completion.

So we need to use Angular CLI to create the service:

$ ng g s services/celebrate

This will create the service in services/celebrate.service.ts .

In CelebrateService, we create a celebrate RXJS Subject that will be used exclusively by the service, and the celebrate$ Observable that will enable us to listen to emissions controlled by that Subject.

The celebrate$ Observable will be subscribed to in CelebrateComponent, so it can react to the celebrate event.

We also define a function startCelebrate() , which will be called when you want to trigger a celebration.

create celebrate.service.ts

That’s it for CelebrateService. Now back to the component…

Provide dependencies via dependency injection

We need to resolve dependencies through the constructor function. I will inject the PLATFORM_ID token, that we use to check that this component isn’t initialised in a SSR environment (as we don’t have access to the HTMLCanvasElement).

We also need to dynamically render the canvas element with Renderer2, using the host element (ElementRef), plus we need the CelebrateService dependency (that we just created), to bind to the celebrate$ observable as discussed.

dependencies

We check that we are client side if using SSR with the isPlatformBrowser , subscribe to the celebrate$ observable, whilst taking care of the subscription object returned from the subscribe() call, with takeUntil .

The main ‘celebrate’ function

I’ve left this function last because this is where the magic happens.

the full celebrate.component.ts file

With the celebrate function, we:

Check the canvas confetti library doesn’t already exist.

the canvas confetti library doesn’t already exist. Append the canvas to the host element (ElementRef) using Renderer2.

the canvas to the host element (ElementRef) using Renderer2. Create the confetti canvas, if not already created, providing the canvas we created.

the confetti canvas, if not already created, providing the canvas we created. Create an Interval, so that the confetti will be called every 200 milliseconds, with setInterval .

so that the confetti will be called every 200 milliseconds, with . Set a timer so we can eventually destroy the confetti, with clearInterval() .

so we can eventually destroy the confetti, with . Fire the confetti 🎉 🎊

BOOM

Full Screen

To make the canvas full screen with your own canvas element, you’ll need to style your component. The canvas positioning needs to be fixed, and to be full width and height.

You can do this inside celebrate.component.scss . As soon as the confetti function is finished, the canvas will be destroyed and removed from the DOM so using fixed is this context won’t cause any issues.

Confetti Configuration

You can configure the particle count, start velocity, spread, origin (where it will fire from), shape (can give you a snow-effect with ‘circle’) plus more! Check out the demo page below to see what’s possible!

— — — — — — — — — — — — — — Done 🚀 — — — — — — — — — — — — —

Feel free to point out anything I’ve missed. Any issues, I will do my best to help!