响应热图

Responsive Heatmaps

问题

我在编写 一些 代码时遇到了一个小问题,我在其中自动化了一个脚本来跟踪用户与 UI 的交互,包括mousemove & click events. If I didn't have to worry about making it responsive, then I could probably call it a day & ship the work that I've done already, however my brain is seeking some wizardry knowledge, I'm struggling to make it super responsive. Here's just a quick example of the kinda thing that I'm working on, it's nothing genius, if anything I it's mostly heatmap.js 这就是繁重的工作。目前我只是看看我是否可以将其作为概念证明而不是其他任何东西...

代码

所以目前,我正在跟踪 event.pageXevent.pageY 值以 准确地存储 事件发生的位置,我还存储了window.innerWidth & window.innerHeight 值到 try & 计算出一些函数,允许我根据其他设备的大小来偏移位置。

例如如果你看上面的示例图片,这对于静态页面来说是完美的,但是如果我说让页面更窄一点,你可以在这里看到它与上面的图片不一致:

无论如何,不​​用多说,这里有一些示例代码:

// A lot of other code...

var setupHeatMaps = function (pages, heatMaps) {
  pages.forEach(function (page) {
    page.addEventListener("click", function (event) {
      heatMaps.push({ x: event.pageX, y: event.pageY, value: 10000 });
      onStateChange();  
    });

    // Don't collect ALL mouse movements, that'd be crazy, so collect 
    // every 1/10 mouse movements.
    var counter = 0;

    page.addEventListener("mousemove", function (event) {
      if (counter === 10) {
        heatMaps.push({ x: event.pageX, y: event.pageY, value: 20 });
        onStateChange();
        counter = 0;
      } else {
        counter ++;
      }
    });
  });
};

// A lot of other code... 

// Curried function so that it can be passed around without exposing the state...
var renderHeatMaps = function (heatMaps) {
  return function () {
    var max = heatMaps.length;
    var points = heatMaps;

    var parent = getParentElement();
    var styleObj = window.getComputedStyle(parent);
    var div = document.createElement("div");
    var body = document.querySelector("body");
    var background = document.createElement("div");

    // This element needs to sit in front of the 
    // background element, hence the higher z-index value.
    div.style.position = "absolute";
    div.style.zIndex = 9;
    div.style.left = "0px";
    div.style.top = "-80px";
    div.style.width = "100vw";

    // Even though this element will sit behind the element
    // that's created above, we will still want this element to 
    // sit in front of 99% of the content that's on the page.
    background.style.position = "fixed";
    background.style.top = "0px";
    background.style.left = "0px";
    background.style.height = "100vh";
    background.style.width = "100vw";
    background.style.zIndex = 5;
    background.style.backgroundColor = "rgba(255, 255, 255, 0.35)";
    background.setAttribute("id", "quote-customer-heat-map-background");

    var heightInPx = styleObj.getPropertyValue("height");
    var rawHeight = parseInt(heightInPx.replace("px", ""));
    var newHeight = parseInt((rawHeight + 80));

    div.style.height = newHeight + "px";
    div.setAttribute("id", "quote-customer-heat-map-foreground");
    body.style.paddingBottom = "0px";
    body.appendChild(background);
    body.appendChild(div);

    var heatMap = h337.create({
      container: div,
      radius: 45
    });
    
    heatMap.setData({ max: max, data: points });
  };
};

// A lot of other code...

由于您可以看到 setupHeatMaps 中使用的 pages 元素的宽度发生变化,因此查看此数据会严重偏移。老实说,我昨天花了 很多 时间思考这个问题,但我仍然没有想到任何似乎合理的事情。

或者

我想知道我是否应该以某种方式只是将页面存储为图像,过度播放热图,那样我就不会真的 不得不担心热图的响应。但后来我需要弄清楚其他一些事情......例如对这些数据进行版本控制,因此如果用户在 phone 上查看页面,它将将该数据单独存储到从他们在笔记本电脑或台式机设备上的前一​​个会话中收集的数据中。

结论

老实说,我不是完全确定最好的行动方案是什么,你们以前遇到过这样的事情吗?你们有没有想过解决这样的问题的天才?

P.S. 我会分享更多代码,但是其中有 大量 代码总体 解决方案,所以我有点不能分享所有这些,我很确定 Whosebug 的人会因此而讨厌我! - 你也可以看出我这样做是作为一个 POC,因为通常我只是将 background 元素和 div 元素的渲染卸载到底层框架,而不是像这样以编程方式进行,记住这一点,这 只是一个 POC.

也许您可以更改设置并记录事件发生在哪个元素上,并保存与该元素相关的位置数据,而不是整个页面。 您仍然记录了页面上的所有事件,但是您保存了与元素相关的数据而不是整个页面,这可以防止您在查看时将数据混合在热图中 它以各种宽度显示在您的网站上。

假设您在伪代码中有这样的设置:

@1000px width
[html width 1000px, height 500px
  [div width 800px, height 400px centered
    [button1 width 100px, height 30px centered] 
  ]
]

@500px width
[html width 500px, height 1000px
  [div width 400px, height 800px centered
    [button1 width 80px, height 30px centered]
  ]
]

您的用户始终在某些元素上移动。只需像这样捕获该数据,示例数据来自两个不同屏幕尺寸的 2 个用户,将光标移向中间的按钮:

user interaction{
  over element: html {dimensions: width 1000px, height 500px}
  user position: {x 900, y 20}
}

user interaction{
  over element: html {dimensions: width 1000px, height 500px}
  user position: {x 850, y 60}
}

user interaction{
  over element: div {width 800px, height 400px}
  user position: {x 700, y 60}
}

user interaction{
  over element: button1 {width 100px, height 30px}
  user position: {x 90, y 10}
}

user interaction{
  over element: html {dimensions: width 500, height 1000px}
  user position: {x 450, y 100}
}

user interaction{
  over element: div {width 400px, height 800px}
  user position: {x 380, y 40}
}

user interaction{
  over element: button1 {width 80px, height 30px} 
  user position: {x 60, y 10}
}

然后当您查看您的网站时,绘制所有元素的热量并计算捕获数据的相对位置。 因此,当您以 @500px 宽度查看您的网站时,button1 上的热量如下:

[button 1 width 80px, height 30px
heat 1, x 72px y 10
heat 2, x 60px y 10
]

并且您对所有其他元素执行相同的操作。我不知道这样的数据有多大用处,但这是为了奇怪的魔法吧?

就我个人而言,我只是根据您查看热图的屏幕宽度排除数据。这样您就可以使用此设置并获得有用的热图数据。因此,您将根据是否以特定的响应宽度捕获数据来排除数据。因此,您至少可以将所有用户数据以高宽度混合在一起,因为您知道您感兴趣的 Web 元素可能仍然居中且大小相同。

将响应式设计分成 2 种或 3 种尺寸、显示器、平板电脑和 phone 是很常见的。您会惊讶地发现,您可以在这 3 个方面保持设计布局的相似性。越相似,混合不同宽度的数据就越有用,这些数据将落入特定的媒体查询范围。

只要您使用与稍后绘制时相同的技术来获取宽度和高度以保存数据,就可以了。例如,即使您忽略边距和边框,您的事件仍会在元素处于边距时捕获到该元素,这样您可以获得这样的数据:用户位置:{x -10,y -1}并仍然使用它在元素上涂上你的热量。

您还可以为用户提供混合和过滤不同大小的用户数据的选项,只需将其称为实验数据混合或其他名称即可。例如,如果您混合使用所有用户数据而不管屏幕尺寸如何,您仍可能会在点击次数最多的元素上获得一些非常有用的视觉信息。

您可以做的另一件事是对结果进行软混合。如果您的用户数据在宽度和高度上与当前元素的尺寸相差太多,比如超过 30%,您可以将其排除。您可以通过让用户设置该百分比的灵敏度(羽化)来使其动态化。这样您仍然可以查看所有较小元素上的所有用户热度,同时它会忽略较大的更易变元素上不太相关的用户事件。

我对这个问题的理解是该项目在正确执行事件、存储信息和显示方面运行良好,但是存在用户体验问题,因为系统正在偏移(及时,我认为)。我会使用一个非常类似于压缩的想法。将您的显示器想象成一个像素网格。很明显存在一些精度问题,因为即使我们使用我们的计算机及其组件允许我们使用的最大精度,如果我们取两个相邻像素并想要在两者之间单击,我们很快就会意识到我们的精度系统报价非常有限,这对于无法遵守 Hausdorf separation.

的数字设备来说是不言而喻的。

现在我们已经通过我们的小思想实验探索了局限性,我们可以承认存在这样的局限性。因此,原则上,降低像素数量而不是事件数量不会引入更多问题,它会降低空间精度,但会降低一定数量(当然,您认为仍然可以接受的数量)。因此,如果您为地图应用了 x * y 分辨率,那么您可以将分辨率视为“每 5 个像素和高度计数”。这将在构成我们压缩基础的实际像素和假想像素之间建立映射,将可点击的位置减少 25 倍。

这样您就可以将显示器视为一组区域,区域的数量明显小于实际像素数量(在我们的示例中为 25x)。这将允许您避免单独存储每个 coordination/click,而是始终存储 (x, y, n),即事件发生区域的中心和事件重复的次数。

这样,如果用户点击位于中心为 (x, y) 的区域中的像素 (x', y'),则只有在未发生点击时才会有新数据在那个地区呢。否则,如果点击发生在那里(不一定是在完全相同的像素,而是在该区域),那么您只需增加 n。因此,您将拥有一组小得多的像素事件编号数据,而不是一组非常大的原始像素事件数据。

这种方法的缺点当然是它在一定程度上降低了几何精度,但它应该有助于数据处理的优化。

我承认我对 Heatmap.js 不是很有经验,所以我不确定它是否 API 支持我的建议。如果有,那么尝试使用它就有意义了。如果没有优化好,或者不支持这样的功能,那么我会根据实际需要使用canvas或者svg来实现热图功能。

虽然这个解决方案可能不是有史以来最优雅的解决方案,但它至少可以工作,如果有人有任何想法或任何更好的想法,请通过一切都意味着筹码!

我的解决方案解释

所以我在想,我 可以 通过 iframe 渲染它,确保它不是有史以来最漂亮的解决方案,但至少这意味着没有复杂的涉及定位逻辑,与我看过的其他一些解决方案相比,它在计算复杂性方面相对较轻,并且它适用于各种屏幕尺寸,本质上 消除了对此的需要响应...有点...

当然,这意味着您将在左、右和中间有滚动条,但这很简单,直截了当,这意味着我可以在 非常 中制作 MVP时间不多,而且实际上,我希望您的普通初级开发人员可能更容易理解发生了什么?你们有什么感想?

我也试图从用户的角度来考虑它,实际上在这种应用程序中,我更喜欢真实、原始的准确性,而不是看起来如此漂亮以至于我想成为一名设计师的东西。我什至对这个主题做了一些功课,因为我 本质上 一直在尝试构建一些会话重播软件,它非常有趣 project/feature 我必须说!

使用 some 样式,我已经能够像这样完成 something...显然这不是直接片段website/application,但这是我老板创建的一些营销 material,但 'laptop' 中的内容是 实际 screenshot/snip 的 Web 应用程序! - 所以总而言之,我认为它看起来还好吗? - 你可以看到溢出发生的地方&屏幕的右侧有点被切断,但是因为 UI sorta 中的其他所有东西都表现得像他们有一个 fixed 位置,我个人认为 似乎 工作得很好。

编辑

我只想感谢所有花时间的人,无论是添加评论还是提供答案,很高兴看到开发社区如此乐于助人!谢谢大家!

博客Post

所以我在这里写了 一点 关于这个主题的更多内容: