当前位置: 首页 > news >正文

前端埋点系统之如何用heatmap.js画网页热力图

Hello,大家好。在当今数字化时代,理解用户行为成为了企业成功的关键之一。随着互联网的发展,用户与网站、应用和产品的互动变得愈发复杂而多样化。在这样的背景下,埋点系统成为了洞察用户行为的重要工具之一。而其中的热力图分析,则更加直观的帮助我们分析用户的喜好。

之前我们介绍了什么是热力图,以及它如何成为理解用户行为的有力工具。今天我们从技术的角度来看,如何实现热力图效果呢?

热力图是埋点系统必不可少的一项能力,可以来看看Webfunny一体化埋点系统的效果

一、有哪些免费工具可以实现热力图效果

热力图主要的实现方式,还是利用目前现有的开源工具,如:百度的echarts、阿里系的G2、还有就是我们今天要说的heatmap.js。

当然,你如果有兴趣和精力,可以自己手搓一个,原理也不是特别复杂,关于热力图的实现原理:

一般可大致归纳为以下几个步骤:

  1. 为每个数据点设置一个从中心向外灰度渐变的圆;

  2. 利用灰度可以叠加的原理,计算每个像素点数据交叉叠加得到的灰度值;

  3. 根据每个像素计算得到的灰度值,在一条彩色色带中进行颜色映射,最后对图像进行着色,得到热力图。

百度echarts的热力图效果

在这里插入图片描述

阿里系的G2热力图效果

在这里插入图片描述

heatmap.js热力图效果

在这里插入图片描述

根据效果来看,其中G2和heatmap.js的热力图效果都比较符合我们的使用场景,heatmap.js已经处理好颜色效果了,所以最后选择了heatmap.js。

二、如何采集网页上的热力数据

热力图在我们的印象中,主要的使用场景是在地图上,比如哪个地方温度,就会呈现红色,哪个地方的温度低就会呈现蓝色。而我们今天要做的是,采集网页上的热力图,主要包含三个指标的数据。

网页上又没有温度,哪来的热力值呢,他们分别是:鼠标点击量、鼠标停留时长、页面元素曝光时长

1. 点击热力数据
点击量热力图和好理解,点击的越多,热力值则越高。采集方式是通过监听全局点击事件,需要采集的几个主要的指标有:

页面地址:这个是用来确定是哪个页面的;

页面宽度/高度:这个是用来确定页面尺寸的;

鼠标点击位置(x坐标,y坐标):这个是用来锁定页面坐标,计算热力值的;

/*** 启动点击事件监听*/
export function startClickRecord() {window.addEventListener('click',function(e){console.log('触发点击事件!', e)if (!e) return/** 检查定时器是否开启 */if (store.timerStatus === 'off') {console.log('定时器已结束,触发鼠标点击,则重新开启')store.timerStatus = 'on';startGlobalTimer()}try {const scrollWidth = (document.body ? document.body.scrollWidth : 0) || window.innerWidthconst weTitle = document.titleconst wePath = Utils.b64Code(Utils.getPath())const weFullPath = Utils.b64Code(Utils.getPath('full'))const weScrollWidth = scrollWidth - scrollWidth % 20const weScrollHeigh = (document.body ? document.body.scrollHeight : 0) || window.innerHeightconst weXPath = Utils.getXPath(e)const wePageX = e.pageXconst wePageY = e.pageY// const weScrollX = window.scrollX// const weScrollY = window.scrollYconst weRatio = parseInt(window.devicePixelRatio)// 上报点击数据const data = {weTitle, wePath, weScrollWidth, weScrollHeigh, weXPath, weFullPath, wePageX, wePageY, weRatio}webfunnyGlobal.webfunnyEvent('Webfunny-Replace-HeatMapClickPointId').trackEvent(data);} catch(e) {console.error('click error', e)}}, true);
}

2. 鼠标停留时长热力图
点击量是直接反映用户达成的目标,鼠标停留时间则是反应了用户的兴趣之所在,也很重要。停留和点击的热力图很相似,只是停留时长的热力图数据会更密集一些。

鼠标停留时间的采集方式跟点击类似,通过监听mousemove事件进行采集,需要采集的几个主要的指标有:

页面地址:这个是用来确定是哪个页面的;

页面宽度/高度:这个是用来确定页面尺寸的;

鼠标点击位置(x坐标,y坐标):这个是用来锁定页面坐标,计算热力值的;

停留时间:这是一个关键性指标,这个值的上限是一个不确定的,但是它的上限对热力值的影响很大。

例如:如何设置很高,就会影响热力的准确性,用户鼠标放在哪里不动,那个点的热力值就会很高,其实他只有一个用户,并不能反馈出有价值的数据;如果设置很低,又无法反馈用户真是的停留时长了,所以这里的做成动态配置的最好。

建议:用户量小的应用,这个上限值设置低一些, 如:1000ms,因为个别用户会造成较大影响;用户量大的应用,设置稍微偏高些,2000ms,大量的用户会让拉平整体的数据,让数据趋于准确。

/*** 启动鼠标停留事件监听*/
export function startMousemoveRecord() {console.log('全埋点,鼠标停留时长,启动')let timerwindow.addEventListener('mousemove',function(e){if (!e) returnif (timer) {clearTimeout(timer)}/** 检查定时器是否开启 */if (store.timerStatus === 'off') {console.log('定时器已结束,触发鼠标移动,则重新开启')store.timerStatus = 'on';startGlobalTimer()}timer = setTimeout(() => {try {const weTitle = document.titleconst scrollWidth = (document.body ? document.body.scrollWidth : 0) || window.innerWidthconst wePath = Utils.b64Code(Utils.getPath())const weFullPath = Utils.b64Code(Utils.getPath('full'))const weScrollWidth = scrollWidth - scrollWidth % 20const weScrollHeigh = (document.body ? document.body.scrollHeight : 0) || window.innerHeightconst weXPath = Utils.getXPath(e)const wePageX = e.pageXconst wePageY = e.pageY// const weScrollX = window.scrollX// const weScrollY = window.scrollYconst weRatio = parseInt(window.devicePixelRatio)const mousemoveInfo = {weTitle, wePath, weScrollWidth, weScrollHeigh, weXPath, weFullPath, wePageX, wePageY, weRatio}// 鼠标移动停留生效console.log('鼠标移动停留生效:', store.mouseStayInfo)if (!store.mouseStayInfo) {// 如果没有记录信息,直接存入内存中store.mouseStayInfo = { ...mousemoveInfo, startTime: new Date().getTime() }} else {// 如果有记录信息,就需要将之前的停留信息放进任务队列,并重新记录当前有效的停留const nowTime = new Date().getTime()const { startTime = 0 } = store.mouseStayInfo || {}let timeDiff = nowTime - startTimeconsole.log('鼠标距离上次时间差:', timeDiff, wePageX, wePageY)if (timeDiff > 150) {// 如果停留时间超过上限, 则默认为上限时间timeDiff = timeDiff > COMMON_FIELD.MOUSE_STAY_LIMIT ? COMMON_FIELD.MOUSE_STAY_LIMIT : timeDiffconst tempMousemoveInfo = { ...mousemoveInfo, stayTime: timeDiff }// TaskQueue.addTask(config.trackUrl, tempMousemoveInfo)// 上报鼠标移动数据console.log('即将执行上报', tempMousemoveInfo)webfunnyGlobal.webfunnyEvent('Webfunny-Replace-HeatMapStopPointId').trackEvent(tempMousemoveInfo);store.mouseStayInfo = { ...mousemoveInfo, startTime: nowTime }}}} catch(e) {console.error('mousemove error: ', e)}}, 200)}, true);// 鼠标离开浏览器后,需要清理历史数据,延迟1s,防止window.addEventListener('mouseout', function(e){var tagName = e.target.tagName ? e.target.tagName.toLowerCase() : ''if (tagName === 'html') {setTimeout(function() {store.mouseStayInfo = ''console.log('鼠标移出了浏览器, 清理鼠标停留数据', store.mouseStayInfo)}, 1000)}}, true)
}

三、如何将热力图覆盖到网页上呢

热力数据采集到了,怎么才能将它们正确的放到网页上呢。

其实也简单,上层是heatmap.js生成的热力效果图;下层是iframe,显示的是网页内容,这样热力图效果就呈现出来了。

在这里插入图片描述

需要注意的是:网页会滚动,宽度也不同,鼠标停留和点击的位置需要取相对位置,而不是绝对位置

四、heatmap.js生成热力图代码

生成heatmap对象,并将热力值数据一个个填充进去就可以了。

heatmap的配置项有很多,下方是我试验出来比较简单的配置项。

安装依赖:npm install heatmap.js --save

import h337 from “heatmap.js”

// 查找元素
const heatEle = document.getElementById(this.state.heatId)
heatmapInstance = h337.create({
container: heatEle,
// radius: 30,
// maxOpacity: 0.7

      radius: 20, // 点的半径// maxOpacity: 0.8, // 最大不透明度// minOpacity: 0.2, // 最小不透明度blur: 0.75, // 模糊半径useLocalExtrema: true, // 是否使用局部极值});this.props.dataList.forEach((item) => {heatmapInstance.addData({x: item.x,y: item.y,value: item.value * 100});})

五、热力图细节优化

heatmap的热力效果虽然不错,但是我并未在文档中找到能提示热力值的API,这就有点尴尬了,因为热力图效果虽然好,但是没有热力值提示,总会让人觉得缺点什么

没办法,只有手动加一个了;虽然heatmap没有提供tip提示,但是却提供了获取热力值的API,这下就简单多了,添加鼠标移动事件,在鼠标位置上方添加div显示热力值,不要忘记延时显示和添加防抖哦

在这里插入图片描述

代码如下:

let mousemoveTimer = 0heatEle.addEventListener("mousemove", (e) => {const { offsetX, offsetY } = econst heatTipCon = heatmapInstance.getValueAt({ x: offsetX, y: offsetY })if (mousemoveTimer) {clearTimeout(mousemoveTimer)mousemoveTimer = setTimeout(() => {this.setState({heatTipCon: heatTipCon / 100, heatTipX: offsetX, heatTipY: offsetY - 20})}, 300)} else {mousemoveTimer = setTimeout(() => {this.setState({heatTipCon: heatTipCon / 100, heatTipX: offsetX, heatTipY: offsetY - 20})}, 300)}})

以上是如何利用heatmap实现热力图效果介绍,感兴趣的同学可以直接访问webfunny前端监控和前端埋点系统
在这里插入图片描述

相关文章:

前端埋点系统之如何用heatmap.js画网页热力图

Hello,大家好。在当今数字化时代,理解用户行为成为了企业成功的关键之一。随着互联网的发展,用户与网站、应用和产品的互动变得愈发复杂而多样化。在这样的背景下,埋点系统成为了洞察用户行为的重要工具之一。而其中的热力图分析&…...

CentOS 7系统下Redis Cluster集群一键部署脚本发布

引言 在大数据和云计算时代,Redis作为一款高性能的键值存储数据库,广泛应用于各种场景。然而,手动搭建Redis Cluster集群过程繁琐且容易出错。为了简化这一过程,本文提供了一个在CentOS 7系统下Redis Cluster集群的一键部署脚本,帮助开发者快速搭建Redis Cluster集群。 …...

自编以e为底的对数函数ln,性能接近标准库函数

算法描述: (1). 先做自变量x的范围检查,不能出现负数和0. 自己使用时,如果能通过其它途径保证自变量为正,那么可以省略这两个判断,提高速度。 (2). 根据IEEE 754浮点数的格式,,则 ln(x)kln(2)ln…...

Java中的日期时间

JDK8之前常用的日期时间类 System.currentTimeMillis():获取当前毫秒数(long类型) java.util.Date:通用Date类 import java.util.Date;Date date new Date(); // 空参构造器 System.out.println(date.getTime()); // 获取当前时…...

位置编码的表示

位置编码的表示位置编码的表示位置编码的表示位置编码的表示位置编码的表示...

0,国产FPGA(紫光同创)-新建PDS工程

国产FPGA正在蓬勃发展,紫光同创FPGA是大家竞赛时经常遇到的一款国产FPGA,本专栏从IP核开始一直到后续图像处理等。 开发板:盘古50K标准板 1,新建PDS工程 点击File(1),然后是New Projects&#…...

c++联合

结构体与联合体的区别 结构体(struct)中所有变量是“共存”的——优点是“有容乃大”,全面;缺点是struct内存空间的分配是粗放的,不管用不用,全分配。 而联合体(union)中是各变量是“互斥”的——缺点就是不够“包容”&#xff…...

Edit Data. Create Cell Editors. Validate User Input 编辑数据。创建 Cell Editors。验证用户输入

Goto Data Grid 数据网格 Edit Data. Create Cell Editors. Validate User Input 编辑数据。创建 Cell Editors。验证用户输入 Get and Modify Cell Values in Code 在代码中获取和修改单元格值 仅当 Grid 及其列已完全初始化时,才使用以下方法。如果需要在表单仍…...

Java 文件操作与IO流

文件 文件有两个概念,在广义来看就是操作系统上对硬件和软件资源抽象为文件。 在侠义上来看,就是我们保存在硬盘上的文件 在这里我们讨论的是狭义的文件,在外面的硬盘上的文件细分又可以分为二进制文件和文本文件,文本文件可以通…...

探索开源MiniMind项目:让大语言模型不再神秘(1)

简介: 声明:本人非此项目作者,仅仅是探索项目,分享项目。如有不妥,请联系我删除! 原项目地址:GitHub - jingyaogong/minimind: 「大模型」3小时完全从0训练26M的小参数GPT,个人显卡即…...

Android 大疆面经

Android 大疆面经 文章目录 Android 大疆面经一面 一面 自我介绍问项目聊了10分钟View的绘制流程MVC,MVP,MVVM的区别view和viewmodel的通信,除了databing还有其他的方式面向对象和面向过程的区别工厂模式和策略模式,哪些框架使用…...

【2024-10-31-2024-11-03】LeetCode刷题——python语法基础题

📝前言说明: ●本专栏主要记录本人的基础算法学习以及LeetCode刷题记录,主要跟随B站作者灵茶山的视频进行学习,专栏中一篇文章为B站对应的一个视频 题目主要为B站视频内涉及的题目以及B站视频中提到的“课后作业”。 ●文章中的理…...

【算法】二分查找

目录 一、概念 二、思路 三、边界问题 一、概念 在一本书中查找某一页,我们总是倾向于先翻到整本书的中间,然后根据当前页数判断我们想要找的页在当前页的左半本中还是右半本中,接着继续翻到剩下半本书的中间...... 这就是二分查找思想在…...

第十五章 Vue工程化开发及Vue CLI脚手架

目录 一、引言 二、Vue CLI 基本介绍 三、安装Vue CLI 3.1. 安装npm和yarn 3.2. 安装Vue CLI 3.3. 查看 Vue 版本 四、创建启动工程 4.1. 创建项目架子 4.2. 启动工程 五、脚手架目录文件介绍 六、核心文件讲解 6.1. index.html 6.2. main.js 6.3. App.vue 一、…...

【Grafana】Grafana 基础入门

Grafana 简介 什么是Grafana Grafana 是一跨平台的开源的可视化分析工具,是目前网络架构和应用分析中最流行的时序数据展示工具,主要用于大规模指标数据的可视化展示。 它是用Go语言开发,可以做数据监控和数据统计,带有告警功能…...

如何获取页面上所有input框

要获取页面上所有的<input>框&#xff0c;你可以使用JavaScript。这通常可以通过查询DOM&#xff08;文档对象模型&#xff09;来实现&#xff0c;有几种方法可以做到这一点&#xff0c;包括使用document.querySelectorAll、document.getElementsByTagName或document.get…...

0-ARM Linux驱动开发-字符设备

一、字符设备概述 Linux 系统中&#xff0c;设备被分为字符设备、块设备和网络设备等。字符设备以字节流的方式进行数据传输&#xff0c;数据的访问是按顺序的&#xff0c;一个字节一个字节地进行读取和写入操作&#xff0c;没有缓冲区。例如&#xff0c;终端&#xff08;/dev…...

使用 Faster Whisper 和 Gradio 实现实时语音转文字

随着人工智能技术的进步&#xff0c;语音识别已经成为最热门的研究领域之一。如何实现高效、准确的实时语音转文字功能&#xff0c;是许多开发者关注的重点。本文将介绍如何使用 Faster Whisper 和 Gradio 这两个强大工具&#xff0c;快速构建一个实时语音转文字应用。 Faster…...

redis v6.0.16 安装 基于Ubuntu 22.04

redis安装 基于Ubuntu 22.04 本文演示如何在ubuntu22.04下&#xff0c;安装redis v6.0.16&#xff0c;并配置测试远程访问。 Step1 更新环境 sudo apt updateStep2 安装redis sudo apt install redis-server -yStep3 启动 sudo systemctl restart redissudo systemctl sta…...

Milvus - 内存索引类型详解

1. 背景概述 在大规模数据处理和向量相似性搜索场景中&#xff0c;内存索引的使用显著提升了查询速度和效率。Milvus 提供了多种内存索引类型&#xff0c;以满足不同场景下的性能需求。本文将介绍 Milvus 支持的各种内存索引类型及其适用场景、配置参数和使用方法。 2. 为什么…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

Xshell远程连接Kali(默认 | 私钥)Note版

前言:xshell远程连接&#xff0c;私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

《基于Apache Flink的流处理》笔记

思维导图 1-3 章 4-7章 8-11 章 参考资料 源码&#xff1a; https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战

在现代战争中&#xff0c;电磁频谱已成为继陆、海、空、天之后的 “第五维战场”&#xff0c;雷达作为电磁频谱领域的关键装备&#xff0c;其干扰与抗干扰能力的较量&#xff0c;直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器&#xff0c;凭借数字射…...

Web 架构之 CDN 加速原理与落地实践

文章目录 一、思维导图二、正文内容&#xff08;一&#xff09;CDN 基础概念1. 定义2. 组成部分 &#xff08;二&#xff09;CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 &#xff08;三&#xff09;CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 &#xf…...

作为测试我们应该关注redis哪些方面

1、功能测试 数据结构操作&#xff1a;验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化&#xff1a;测试aof和aof持久化机制&#xff0c;确保数据在开启后正确恢复。 事务&#xff1a;检查事务的原子性和回滚机制。 发布订阅&#xff1a;确保消息正确传递。 2、性…...

根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要

根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分&#xff1a; 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...

Java 与 MySQL 性能优化:MySQL 慢 SQL 诊断与分析方法详解

文章目录 一、开启慢查询日志&#xff0c;定位耗时SQL1.1 查看慢查询日志是否开启1.2 临时开启慢查询日志1.3 永久开启慢查询日志1.4 分析慢查询日志 二、使用EXPLAIN分析SQL执行计划2.1 EXPLAIN的基本使用2.2 EXPLAIN分析案例2.3 根据EXPLAIN结果优化SQL 三、使用SHOW PROFILE…...

电脑桌面太单调,用Python写一个桌面小宠物应用。

下面是一个使用Python创建的简单桌面小宠物应用。这个小宠物会在桌面上游荡&#xff0c;可以响应鼠标点击&#xff0c;并且有简单的动画效果。 import tkinter as tk import random import time from PIL import Image, ImageTk import os import sysclass DesktopPet:def __i…...

路由基础-路由表

本篇将会向读者介绍路由的基本概念。 前言 在一个典型的数据通信网络中&#xff0c;往往存在多个不同的IP网段&#xff0c;数据在不同的IP网段之间交互是需要借助三层设备的&#xff0c;这些设备具备路由能力&#xff0c;能够实现数据的跨网段转发。 路由是数据通信网络中最基…...