skip to Main Content

I have been creating a heatmap feature for my own dashboard which will show heatmaps across the pages.

In the frontend script I have been saving them like this by getting these needed attributes

  function clickHandler(e) {
        let target = e.target || e.srcElement;
        const
            rect = target.getBoundingClientRect(),
            elementX = e.clientX - rect.left,
            elementY = e.clientY - rect.top,
            elementWidth = rect.width,
            elementHeight = rect.height;
        //
        const
            fullPageX = e.pageX,
            fullPageY = e.pageY,
            body = document.body,
            html = document.documentElement,
            width = window.innerWidth,
            height = Math.max(body.scrollHeight, body.offsetHeight,
                html.clientHeight, html.scrollHeight, html.offsetHeight);
        //
        rrweb.record.addCustomEvent("click", {
            element: {
                x: elementX,
                y: elementY,
                elementWidth,
                elementHeight,
            },
            fullpage: {
                x: fullPageX,
                y: fullPageY,
                innerWidth: width,
                innerHeight: height,
            },
        });
        console.log(elementX, elementY, width, height, fullPageX, fullPageY, elementWidth, elementHeight);
    }

I have been saving them into database so I can retrieve them from the api.
Example of a retrieved data.

      {
    "x_coord": 629,
    "y_coord": 1201,
    "height": 959,
    "width": 1920,
    "element_width": 498,
    "element_height": 508,
    "x_element": 466.0279541015625,
    "y_element": 85.62030029296875,
    "page_height": 5781,
    "click_count": "1"
}

{
    "x_coord": -500,
    "y_coord": 1227,
    "height": 959,
    "width": 1920,
    "element_width": 115,
    "element_height": 83,
    "x_element": 63.191253662109375,
    "y_element": 40.5150146484375,
    "page_height": 5781,
    "click_count": "1"
}

I group them by

GROUP BY x_coord, y_coord, width, height, element_width, element_height,x_element, y_element,page_height

The problem lies with scaling the x,y values when the users are on a different screen size resolution

I have been following these answers to implement the saving of the metrics
How to recalculate x,y coordinates based on screensize But it does not seem to correctly work when I am trying to scale the x y on a different size.


The frontend part is made with angular 8 where we used heatmap.js to construct the heatmap, the container where is shown is not full width because I have sidenav and other elements but we can take it as it will be 800px wide. What I need to do is to scale the backgroundImage (width:~1520px and height is the full page height which we use a bot to make it ~8000px with the container on my dashboard, and then construct the coordinates corresponding to that container.

when users screen is the same with screenshot the points are shown correctly but when the screen is with size lower or higher than the screenshot the points shifts by the X value and Y value.

What am i missing?
Scaling Logic =>

let width = coordinates[i].width;
let height = coordinates[i].page_height;
let x = width / 2 + coordinates[i].x_coord;
let y = coordinates[i].y_coord;

let widthRatio = nativeElement.clientWidth / width;
let heightRatio = nativeElement.clientHeight / height;

userData.push({
  x: Math.round(x * widthRatio),
  y: Math.round(y * heightRatio),
  value: parseInt(coordinates[i].click_count),
});

2

Answers


  1. start by determining the actual size of the area where you want to display the heatmap, which could be different from the full screen due to elements like a sidebar or margins assign these containerWidth and containerHeight.

    loop through each set of coordinates you have collected. For each set calculate how much smaller or larger the heatmap display area is compared to the original size where the click was recorded this is done by comparing the display area’s width and height to the original recorded values, creating a width ratio and a height ratio.

    apply these ratios to the original x and y coordinates to scale them to fit the new size. Essentially, you’re resizing the click points so they appear in the correct location on the smaller or larger display.

    // Define the dimensions of the heatmap container
    let containerWidth = nativeElement.clientWidth; // Use the actual width of the container
    let containerHeight = nativeElement.clientHeight; // Use the actual height of the container
    
    // Iterate through data points
    coordinates.forEach(coord => {
        let originalWidth = coord.width;
        let originalHeight = coord.page_height;
    
        let widthRatio = containerWidth / originalWidth;
        let heightRatio = containerHeight / originalHeight;
    
        let scaled = Math.round(coord.x_coord * widthRatio);
        let scaledY = Math.round(coord.y_coord * heightRatio);
    
        userData.push({
            x: scaled,
            y: scaled,
            value: parseInt(coord.click_count, 10)
        });
    });
    
    Login or Signup to reply.
  2. I would use a D3 scale for that:

    scale = d3.scaleLinear([1, 10], [100, 200])
    //                     ^^^^^^^  ^^^^^^^^^^
    //                     A        B
    

    scale is a function that maps a value in a domain (A) (the set of possible input) to another value in a range (B) (e.g the set of possible output). e.g.,

    scale(5)   //=> 144.44444444444446
    scale(5.8) //=> 153.33333333333334
    

    Let’s plot the value 5 on different x axes:

    var scale1 = d3.scaleLinear([1, 10],[1, 100]);
    var scale2 = d3.scaleLinear([1, 10],[1, 200]);
    var scale3 = d3.scaleLinear([1, 10],[1, 300]);
    
    document.querySelector('#x1').innerHTML =
      `<span style="left:${scale1(5)}px">🤓</span>`;
      
    document.querySelector('#x2').innerHTML =
      `<span style="left:${scale2(5)}px">🤓</span>`;
      
    document.querySelector('#x3').innerHTML =
      `<span style="left:${scale3(5)}px">🤓</span>`;
    div {position: relative; height: 25px; border-bottom: 1px solid black}
    span {position: absolute}
    #x1 {width: 100px}
    #x2 {width: 200px}
    #x3 {width: 300px}
    <script src="https://d3js.org/d3.v7.min.js"></script>
    <div id="x1"></div>
    <div id="x2"></div>
    <div id="x3"></div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search