不说废话!直接上场景,例如:
当我们下载APP时,一般会浏览APP的介绍页面,而且肯定会有点击操作,根据某部分或者某个点在这个页面点击的次数,生成对应的点击范围热力图,从而达到反映用户操作行为的功能;
模拟效果如下:
经过分析,我认为主要有两点需要注意:
1、一般像APP页面或者网页,都是拥有很大的流量,所以点击次数肯定都是百万级的;
2、热力值肯定要根据点击次数做出对应的调整;
所以,问题的关键就是怎么生成对应坐标的热力值?
一般的思路是,设定好每次点击的热力值,然后在当重复点击同一坐标时进行累加;
错误的思路肯定是不行了,所以上面的pass!为啥不行就留给读者自己探索了~
现在来说下我的思路:
1、首先既然数据都是百万级的所以肯定不能给每一个坐标都进行处理,因此我们要限定要渲染的坐标,以手机移动端为例,我先限定要生成热力图的部分分成50*200=10000个小格子,然后将每个小格子的中心点作为我将要渲染的坐标,这样不管来多少条数据,我最多只需要渲染10000个坐标即可!(注意:这样就说明用户每次点击的坐标如果在某个小格子的范围内都是会被替换成小格子中心点坐标的)
2、那就是热力值的计算了:
(1)首先我设定最大热力值为1000(这个随意,推荐选个好计算的值);
(2)然后所有被点击坐标的点击次数设为:index。其中肯定有最大值,设为:clickMax;
(3)所以热力值计算公式就出来了:
hotNum = index/clickMax*1000;
这样就可以适应到不同点击次数下的热力值了。
有更好的方法,欢迎留言~
话不多说,开始码代码:
页面结构及样式
html+css:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style>
body {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
#heatmap {
border: 1px solid #ccc;
width: 440px;
/* height: calc(100vh - 2px) */
height: 652px;
}
</style>
</head>
<body>
<button id="btn">生成热力图</button>
<div id="heatmap">
<img src="./img/复仇者.jpg" alt="">
</div>
</body>
</html>
JS部分:
首先将要生成热力图部分成50*200个小格子
(1)遍历出所有的横坐标
var heatmapBox = document.getElementById("heatmap");
var width = heatmapBox.offsetWidth;
var height = heatmapBox.offsetHeight;
var points = [];
var minWidth = width / 50;
var minHeight = height / 200;
// 遍历出所有的横坐标
var xList = [];
var xNum = minWidth / 2;
var xNumConst = minWidth / 2;
for (var i = 0; i < 50; i++) {
xList.push(xNum);
xNum += minWidth
}
(2)遍历出所有纵坐标
// 遍历出所有的纵坐标
var yList = [];
var yNum = minHeight / 2;
var yNumConst = minHeight / 2;
for (var i = 0; i < 200; i++) {
yList.push(yNum);
yNum += minHeight
}
(3)将横纵坐标合并,生成最终的10000个坐标
// 遍历出所有的小格子坐标
var xyList = [];
for (var a = 0; a < xList.length; a++) {
for (var b = 0; b < yList.length; b++) {
var xyItem = {
x: xList[a],
y: yList[b]
};
xyList.push(xyItem)
}
}
(4)当用户点击页面时,记录所点击坐标的点击次数
var clickPointLIst = []; // 用来存储坐标被点击次数
// 当图片被点击时,记录所点击坐标的点击次数
$("#heatmap").on("click", function (e) {
var pageX = e.pageX;
var pageY = e.pageY;
console.log(pageX, pageY);
var clickXY = isNewXY(pageX, pageY);
console.log(clickXY);
if (clickPointLIst.length > 0) {
var isflag = 0;
clickPointLIst.forEach(function (item, index) {
var x = item.x;
var y = item.y;
if (x == clickXY.x && y == clickXY.y) {
isflag++;
clickPointLIst[index].index = clickPointLIst[index].index + 1
}
});
if (isflag == 0) {
clickPointLIst.push({
x: clickXY.x,
y: clickXY.y,
index: 1
})
}
} else {
clickPointLIst.push({
x: clickXY.x,
y: clickXY.y,
index: 1
})
}
});
(5)根据被点击坐标,返回该坐标对应的格子中心坐标
// 根据点击点坐标,生成实际应该起作用的坐标
function isNewXY(pageX, pageY) {
var clickXY = {};
xyList.forEach(function (item, index) {
var x1 = item.x - xNumConst;
var y1 = item.y - yNumConst;
var x2 = item.x + xNumConst;
var y2 = item.y + yNumConst;
if ((pageX >= x1 && pageX <= x2) && (pageY >= y1 && pageY <= y2)) {
clickXY = {
x: item.x,
y: item.y
}
}
});
return clickXY
}
(6)计算出热力值
// 计算出坐标应该对应的热力值
function calcHotNum(clickXY, list) {
var hotNum = 0;
var max = 0;
var thisIndex = 0;
list.forEach(function (item, index) {
max = Math.max(max, item.index);
var x = item.x;
var y = item.y;
if (x == clickXY.x && y == clickXY.y) {
thisIndex = item.index
}
});
hotNum = thisIndex / max * 1000;
return hotNum
}
(7)最终点击按钮生成热力图时,调用上面的方法,给每个坐标赋上热力值
// 点击坐标生成热力图
$("#btn").on("click", function () {
clickPointLIst.forEach(function (item, index) {
var hotNum = calcHotNum(item, clickPointLIst);
item.value = hotNum
});
// console.log(clickPointLIst);
points = JSON.parse(JSON.stringify(clickPointLIst));
points.forEach(function (item, index) {
delete item.index;
item.x = Math.floor(item.x);
item.y = Math.floor(item.y);
// 很诡异,在工作的时候不需要取整,
// 结果回家写博客的时候,发现居然坐标不能是小数????
});
console.log(points);
// heatmap坐标值不能为小数
createHotMap(1000, points)
});
(8)生成热力图
// 热力图生成
function createHotMap(max, points) {
var heatmapInstance = h337.create({
container: document.querySelector('#heatmap'),
});
var data = {
max: max,
data: points
};
console.log(data)
heatmapInstance.setData(data)
};
效果:
(1)在热力图对应的图片区域,点击
(2)点击按钮生成热力图