Integrating Heatmap.js with Mouse-over click in Angular
Tags: angular, javascript, showdev, webdev
Published: 2020-01-07 12:24:44

---
title: Integrating Heatmap.js with Mouse-over click in Angular
published: true
description: Ever wondered how analytics tools create heatmaps? Well, here's good news. I have implemented heatmap in Angular 8 with the help of heatmap.js. Also, added a mouse-over click functionality.
tags: angular, javascript, showdev, webdev
cover_image: https://res.cloudinary.com/idr/image/upload/fl_lossy,q_50/github-repo/angular-heatmap-click-demo_dxbznn.gif
---
A heatmap (or heat map) is a graphical representation of data where the individual values contained in a matrix are represented as colors. There are various types of heatmaps available, but we will stick with the heatmap used for depicting and analyzing user behavior. This type of heatmap renders user interaction density on a container (or an image). Generally, the area with red color means more density i.e. heavy user interactions.

[heatmap.js](https://www.patrick-wied.at/static/heatmapjs/) is a lightweight, easy to use JavaScript library to visualize these user interactions! We will first integrate heatmap.js in our Angular-8 project then add a mouse-over click feature to fetch the points around the mouse pointer. The final product can be visualized in the following gif or [demo link](https://idris-rampurawala.github.io/angular-heatmapjs/):
![Angular-Heatmapjs implementation demo](https://res.cloudinary.com/idr/image/upload/fl_lossy,q_50/github-repo/angular-heatmap-click-demo_dxbznn.gif)

Let's get started :grin:
### Prerequisites
- We will assume that you have a basic knowledge of Angular framework
- This post only aims to guide with the logic of implementation and hence showcasing only `code snippets`. For overall code implementation, check out my [GitHub repository](https://github.com/idris-rampurawala/angular-heatmapjs).

### Integrating heatmap.js with Angular
This is rather a simple step once you look into the [heatmap.js documentation](https://www.patrick-wied.at/static/heatmapjs/docs.html).

#### 1. Install heatmap.js
Heatmap is hosted on `npm` so we can easily install it via `npm` command
`npm install heatmap.js`

#### 2. Preaparing our HTML template of component
We will first create a container in our `HTML` part of the component to load the heatmap graph.
```html
<div id="heatmapContainer">
 <!-- Overlay is for the backgorund. You can even add image instead -->
 <div class="overlay"></div> 
</div>
```
#### 3. Integrating with a component
Next is to create an instance of heatmap inside our component. `h337` is the name of the global object registered by heatmap.js. We can use it to create heatmap instances. We will refer to this object by declaring a variable below our imports
```ts
import { Component, OnInit } from '@angular/core';

declare let h337: any; //heatmap.js global object

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
 gradientCfg = { // heatmap gradient color range
    '0.15': '#6ad180', // green
    '0.25': '#7cd573',
    '0.35': '#90d865',
    '0.45': '#a4da57',
    '0.55': '#badc48',
    '0.65': '#c9cf35',
    '0.75': '#d6c226',
    '0.80': '#e2b41c',
    '0.85': '#e2961d',
    '0.90': '#dd7826',
    '0.95': '#d25c30',
    '1.0': '#c24039' // highest red
  };
  heatmap: any = null; // heatmap instance
  coordinates: Array<Coordinate> = []; // heatmap coordinates array
  heatmapContainer: HTMLElement;  // heatmap container HTML element
...
```

Heatmap coordinates i.e. the data that will be passed to create heatmap instance will be of type:
```ts
export interface Coordinate {
  x: number;
  y: number;
  value: number;
}
```

We will have to create some dummy coordinates to render a promising heatmap graph. We can create a function like below that will generate a handful of coordinates.
```ts
generateCoordinates(): void {
  const extremas = [(Math.random() * 1000) >> 0, (Math.random() * 1000) >> 0];
  const max = Math.max.apply(Math, extremas);
  const min = Math.min.apply(Math, extremas);
  for (let i = 0; i < 1000; i++) {
    const x = (Math.random() * HEATMAP_WIDTH) >> 0;
    const y = (Math.random() * HEATMAP_HEIGHT) >> 0;
    const c = ((Math.random() * max - min) >> 0) + min;
    // add to dataset
    this.coordinates.push({ x: x, y: y, value: c });
  }
}
```
Lastly, we will create instance of heatmap.js `h337.create()` function passing dummy datasets created above.
```ts
ngOnInit(): void {
  this.generateCoordinates(); // this will create dummy coordindates
  const heatmapConfig = { // heatmap config object. For more info visit documentation
    container: document.querySelector('#heatmapContainer'),
    opacity: .8,
    radius: 7,
    visible: true,
    gradient: this.gradientCfg,
    backgroundColor: 'inherit'
  };
  this.heatmap = h337.create(heatmapConfig); // creating the instance
  this.heatmap.setData({ max: 30, data: this.coordinates }); // passing the dummy coordinates
...
```
Voila! We have created a heatmap from dummy dataset. Heatmap.js creates a `canvas` element in our container which will hold the heatmap graph.


### Implementing Mouse-over click on the heatmap
We are done with the initial integration step now comes the tricky part of implementing mouse-over click functionality on the heatmap. The logic is to create a tooltip on the heatmap and a circular container (referred to as `mousecircle` in this post) around the mouse pointer to highlight the area of which the coordinates will be fetched when clicked. Let's get going.

#### 1. HTML template setup
To track the mouse pointer movements, we will create some mouse listeners on our heatmap container, hence the `heatmapContainer` can be modified as
```html
<div id="heatmapContainer" (mousemove)="heatmapMouseMove($event)" (mouseleave)="heatmapMouseOut()"
  (mouseenter)="heatmapMouseEnter()" (click)="mouseCircleClick($event)">
  <div class="overlay"></div>
</div>
```
**:blue_book: NOTE**
We are using `mouseenter/mouseleave` over `mouseover/mouseout` because of 2 major reasons:
1. `mouseenter/mouseleave` does not bubble.
2. Transitions inside the element, to/from descendants, are not counted. This helps us avoid unncessary blackouts on tooltip and mouseover containers as they are inside the heatmap contianer.

#### 2. Adding tooltip and mousecircle
The logic is to add the tooltip and mousecircle on `ngOnInit()` via `Renderer2` so it appears on top of our heatmap rendered canvas.
```ts
ngOnInit(): void {
  // heatmap integration code
  ...
  this.heatmapContainer = document.querySelector('#heatmapContainer');
  this.tooltip = this.renderer.createElement('div'); // tooltip element variable
  this.renderer.addClass(this.tooltip, 'heatmap-tooltip');
  this.renderer.setStyle(this.tooltip, 'display', 'none');
  this.renderer.setStyle(this.tooltip, 'transform', 'translate(39px, 489px)');
  this.mouseCircle = this.renderer.createElement('div'); // mousecircle element variable
  this.renderer.addClass(this.mouseCircle, 'mouseCircle');
  this.renderer.setStyle(this.mouseCircle, 'display', 'none');
  this.renderer.setStyle(this.mouseCircle, 'transform', 'translate(39px, 489px)');
  this.renderer.appendChild(this.heatmapContainer, this.tooltip);
  this.renderer.appendChild(this.heatmapContainer, this.mouseCircle);
}
```

Our logic for all the mouse listeners added in the HTML template above can be summarised as:
<dl>
  <dt><em>mouseenter</em></dt>
  <dd>This event will track whether the mouse pointer is inside our heatmap container.</dd>
  <dt><em>mouseleave</em></dt>
  <dd>This event will track whether the mouse pointer is moved out of our heatmap container. If it turns out to be true, then we will immediately hide our tooltip and mousecircle containers</dd>
  <dt><em>mousemove</em></dt>
  <dd>This event will continuously update the coordinates of our tooltip and mousecircle containers whenever mouse pointer is moved inside our heatmap container.</dd>
</dl>

#### 3. Fetching the coordinates on mouse click
The last part is to fetch all the heatmap coordinates inside the mousecircle area. The idea is to *compare the radius of the circle with the distance of its center from the XY coordinates clicked for each of the heatmap coordinates*. If it lies inside the mousecircle area then just check if it is present in heatmap coordinate.

You can check my [GitHub repository](https://github.com/idris-rampurawala/angular-heatmapjs) for the implementation code.

### Useful Links :star:
- [See it in action here](https://idris-rampurawala.github.io/angular-heatmapjs/)
- [GitHub repository for this project](https://github.com/idris-rampurawala/angular-heatmapjs)
- [Heatmap.js GitHub Repo](https://github.com/pa7/heatmap.js)
- [Heatmap.js documentation](https://www.patrick-wied.at/static/heatmapjs/docs.html)

---

If you like my post do not forget to hit :heart: or :unicorn:

See ya! until my next post :yum: