Integrating Heatmap.js with Mouse-over click in Angular
Idris Rampurawala
Posted on January 7, 2020
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 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:
Let's get started 😁
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.
Integrating heatmap.js with Angular
This is rather a simple step once you look into the heatmap.js documentation.
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.
<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
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:
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.
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.
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
<div id="heatmapContainer" (mousemove)="heatmapMouseMove($event)" (mouseleave)="heatmapMouseOut()"
(mouseenter)="heatmapMouseEnter()" (click)="mouseCircleClick($event)">
<div class="overlay"></div>
</div>
📘 NOTE
We are using mouseenter/mouseleave
over mouseover/mouseout
because of 2 major reasons:
-
mouseenter/mouseleave
does not bubble. - 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.
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:
- mouseenter
- This event will track whether the mouse pointer is inside our heatmap container.
- mouseleave
- 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
- mousemove
- This event will continuously update the coordinates of our tooltip and mousecircle containers whenever mouse pointer is moved inside our heatmap container.
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 for the implementation code.
Useful Links ⭐
- See it in action here
- GitHub repository for this project
- Heatmap.js GitHub Repo
- Heatmap.js documentation
If you like my post do not forget to hit ❤️ or 🦄
See ya! until my next post 😋
Posted on January 7, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
June 30, 2021