最近公司让做一款统计移动APP的热力图插件,好吧百度了下也只是找到了百度统计的一些线索。思考了几天终于开始做了。
首先我说下思路和开发遇到的问题:
一. 数据采集部分:
1.因为是移动端第一个考虑的便是分辨率的问题。毕竟市场上手机屏幕大小区别还是很大的。2. 第二个要考虑的是坐标点的问题一个1980*1080的屏有200万个象素点,好吧我开始蛋疼了,难道为了一个界面保存200万个坐标的信息?3. 第三毕竟是手机端啊,我们的考虑流量的问题,不可能一次弄一个长度200万的数组传后台去,想想都不可能。4. 那么第四个问题来了,我们多久传一次呢? 5.第五个问题,手机可以竖着用,还可以横着用,这又怎么算? 。。。其他问题也是有的目前可以忽略不计。。。 答案来了:我解决的方法是分块,将一个屏幕分了1200块不就是30*40,我将所有的分辨率都分成30*40;一下将200万变成了1200,好了现在轻松多了,通过JS迭代器去统计点击次数,好嘛,流量的问题就这么解决了。考虑到不同APP使用的时候需要的时间不一样我觉定这个time 让APP的开发者去决定,当然了默认值是10秒,或者当前页面关闭的时候传输一次数据。
二.后台(数据处理部分):
前端传过来的数据是采用日志的形式写文件,然后呢定时处理入库。这个参考的是百度统计的做法。
三.展示热力图:
heatmap.js我用的这个开源插件;
实现:
好吧!看到这里你也应该猜到了我做的数据采集部分。
先给大家看下我写的JS:
<pre name="code" class="javascript">/*
* yc
* 1.0
* 2015/11/11
*/
/*
* 热力图坐标采集
*/
(function($) {
/*
* MAP构造
*/
var Map = function() {
this.keys = new Array();
this.data = new Object();
this.put = function(key, val) {
if (this.data[key] == null) {
this.keys.push(key);
}
this.data[key] = val;
};
this.clear = function() {
this.keys = [];
this.data = {};
}
this.toString = function() {
var s = "{";
for (var i = 0; i < this.keys.length; i++) {
var k = this.keys[i];
s += k + ":" + this.data[k];
i == (this.keys.length - 1) ? '' : (s += ",");
}
s += "}";
return s;
};
};
//算法和初始化方法
jQuery.utility = {
getWindowsWH: function() {
var ret = {};
ret.width = $(window).width();
ret.height = $(window).height();
console.log(ret.width + "px," + ret.height + "px");
return ret;
},
setPoint: function(x, y, a, cx, cy) {
return Math.floor(y / cy) * a + Math.floor(x / cx);
},
PointArray: function() {
console.log("初始化 PointArray");
return new Array();
},
PointMap: function() {
console.log("初始化 PointMap");
return new Map();
},
getImg: function() {
return new Image(1, 1);
}
};
//参数配置
var defaults = {
a: 30,
b: 39,
time: 10,
url: '', //get请求URL
pluginID: '', //插件ID
pageNum: 1 //页面编号
};
$.fn.extend({
RLT: function(options) {
var opts = $.extend(defaults, options);
var wh = $.utility.getWindowsWH();
var pointArray = $.utility.PointArray();
var pointMap = $.utility.PointMap();
var cx = Math.floor(wh.width / opts.a); //分块大小
var cy = Math.floor(wh.height / opts.b);
//数据清空
var DataClear = function() {
pointArray = [];
pointMap.clear();
console.log("---清空数据---");
};
//通过Image对象请求后端脚本
var url_ = defaults.url + "?pluginID=" + defaults.pluginID + "&pageNum=" + defaults.pageNum + "&data=";
var Post_ = function(u, data) {
var img = $.utility.getImg();
img.src = u + data;
};
//整理数据
var getData = function() {
if (pointArray.length == 0) {
return null;
};
for (var i = 0; i < pointArray.length; i++) {
var val = 0;
for (var j = 0; j < pointArray.length; j++) {
if (pointArray[i] == pointArray[j]) {
val++;
}
}
pointMap.put(pointArray[i], val);
}
console.log("data:" + pointMap.toString());
return pointMap.toString();
}
return this.each(function() {
//主体函数
$(this).on('mousedown', function() {
var scrollTop = $(this).scrollTop();
var x = window.event.clientX;
var y = window.event.clientY + scrollTop;
var index = $.utility.setPoint(x, y, defaults.a, cx, cy);
pointArray.push(index);
console.log(index + ":(" + x + "," + y + ") pointArray.size-->" + pointArray.length);
});
//定时发送数据
setInterval(function() {
var data = getData();
if (data != null) {
Post_(url_, data); //发送数据
DataClear(); //清空数据
}
}, defaults.time * 1000);
//页面关闭时发送数据
window.onbeforeunload = function() {
var data = getData();
if (data != null) {
Post_(url_, data); //发送数据
DataClear(); //清空数据
}
};
});
}
});
//解析配置文件执行方法
if (config_) {
for (var i in config_.dom) {
$(config_.dom[i]).RLT({ //执行方法
time: config_.time,
pluginID: config_.pluginID, //插件ID
//pageNum: i //页面编号
});
}
} else {
return null;
}
})(jQuery);
页面引用部分:
<script type="text/javascript">
var config_ = {
dom: ['body'],
time: 10,
pluginID: '12AC89F', //插件ID
pageNum: 1211 //页面编号
};
(function(){
var f = document.createElement('script');
f.type = 'text/javascript';
f.src = 'js/relitu2.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(f, s);
})();
</script>
结果图:
好了这个请求就是我们需要的数据