之前完成了一个比较复杂的功能。但是一直没有时间(懒)把这个代码分享出来,趁着一个人加班的机会顺便写个博客。下面是实现的具体展示。
这个图是用来展示机器在一定时间内参数修改的次数。就是说一定时间范围内,两次修改的间隔在五分钟之内,则把他们归为同一个色块。横座标是统计时间的跨度,纵座标是这段时间内修改的次数。
同样颜色的色块代表的是同一台机器,所以就可以非常直观明了的表现机器修改的次数,如果在比较短的时间内出现大量改动,那么就会出现非常细长的色块。我觉得这个功能非常好,虽然需求是领导从别的公司借鉴过来的,不过具体实现都是我自己完成的,花了我两天时间。
好了,不吹牛了,上代码。
首先是在后台统计,统计某时间内机器修改次数。主要就几个参数,机器id,开始时间,结束时间,持续时间,修改次数。统计过后按开始时间正序排序,不然的话形成的图形就比较乱。
然后是前台代码,这个是入口
$(function(){
loadParaChart();
});
function loadParaChart(){//主方法
var query = getFormData();//查询条件,主要是开始时间和结束时间
query["top"] = 10;
$.ajax({
type:"get",
url:url,
data:query,
dataType:"json",
success:function(result){
if(result.statusCode == 200){
var list = result.data;
var o = calculateDataChart(list);//将后台数据处理成echart需要的数据
//总的开始时间
var startTime = +new Date(query.startTime.replace("-","/"));
//总的结束时间,因为是yyyy-MM-dd的格式,所以我加了一天
var endTime = +new Date(query.endTime.replace("-","/")) + 24*60*60*1000;
loadChart(o,startTime,endTime,o.max)//生成图表
}else{
commPageAjaxDone(result);
}
}
});
}
这个是计算色块的长宽和位置,主要是避免重叠,具体原理是把计算后的色块加入到列表里,然后新加入的色块先给个固定位置,然后遍历已有的色块,如果和某个色块重叠则把新色块的位置提到该色块的下面,然后重新遍历,直到新色块和所有旧色块不重叠为止。依次类推。当然还是有优化的空间的。我只做了初步的优化,比如先把横向方位不会重叠的色块排除。
var baseHeight = 0.5;//色块的基本长度
function getMax(count){//获得色块的长度
return baseHeight + 0.05 * count;
}
function calculateDataChart(list){
var data = [];
var max = 1;
var machineMap = {};
var machines = [];
var colorList = ["#588d81" ,"#a6fb52","#1871c6","#e18213","#60ec4c" ];//颜色列表,前五个机器固定颜色
$.each(list,function(i, n){//将后台的数据处理后变成色块的位置信息
var duration = n.duration;
if(duration < 60000) duration = 60000;//色块有最小宽度,避免太窄而无法被选中
var temp = getMax(n.count);//获得色块长度
var color = "";
if(machineMap.hasOwnProperty(n.machineId)){
color = machineMap[n.machineId];
}else{
if(colorList.length > 0){
color = colorList.shift();
}else{
color = randomColor();//颜色用完后,后面的机器随机颜色
}
machines.push({name:n.machineName,color:color});
machineMap[n.machineId] = color;
}
var index = dealOverlay(data, n);//获得色块的y轴起始位置,核心代码
//计算整个座标系的最大高度
if(max < temp + baseHeight + index + 1) max = temp + baseHeight + index + 1;
data.push({
name: n.machineName,
machineId:n.machineId,
startTime:n.startTime,
endTime:n.endTime,
value: [
index,
n.startTime,
n.startTime + duration,
n.duration,
n.count
],
itemStyle: {
normal: {
color: color
}
},
});
});
return {
machines:machines,
data:data,
max:max
};
}
//oldObject 已经被计算过确定位置的色块
//n 新加入的色块
function dealOverlay(oldObject, n){
var data =$.grep(oldObject,function(o,i){//首先过滤掉时间轴不会重复的老色块,因为不会影响新色块的纵座标位置
if (n.startTime > o.value[2] || n.endTime < o.value[1])
return false;
return true;
});
var index = 0.5;
index = overlay(data, n, index);//重叠算法计算新色块的高度
return index;
}
function overlay(data, n, index){//重叠算法
$.each(data,function(i, o){//o 某色块对象
var value = o.value;
//index起始位置大于该色块的最大高度,或者新的色块最大高度小于该色块的起始位置,也就是不重复
if(index > value[0] + getMax(value[4]) || index + getMax(n.count) < value[0]){
return true;
}else{//如果重复了,那么将index改成该色块的最大高度加上一个固定高度,然后再重新遍历计算
//注意,index改变后必须重新遍历一遍,因为之前index不重叠的色块,index改变后有可能会重叠
index = overlay(data, n, value[0] + getMax(value[4]) + baseHeight);
return false;
}
});
return index;
}
然后是echart的代码
function loadChart(obj,startTime,endTime,max){
var machines = obj.machines;
data = obj.data;
var duration = (endTime - startTime);
var series = [{
type: 'custom',//类型,自定义
renderItem: renderItem,//自定义图形设置,关键代码
itemStyle: {
normal: {
opacity: 0.8
}
},
label:{
normal: {
show: true,
position: 'inside',
formatter:function(a){
var data = a.data;
if(a.value[3] > duration/20){
var str = data.name + "在" +transformTime(a.value[3])+ "内修改了" +a.value[4] + "次";
var n = parseInt(9 * 20 * a.value[3]/duration);
var reg = eval("/.{"+n+"}\x01?/g");
str=str.replace(/[^\x00-\xff]/g,"$&\x01").replace(reg,"$&\n").replace(/\x01/g,"");
return str;
}
return "";
},
color:"black"
}
},
encode: {
x: [1, 2],
y: 0
},
data: data
},
];
$.each(machines,function(i,n){//因为自定义模式没有图例,所以我借用了bar模式的图例
series.push({
name:n.name,
type:'bar',
itemStyle: {
normal: {
color: n.color, //这里的图例要注意,颜色设置和仪表盘的颜色对应起来
}
}
});
});
// Generate mock data
var option = {
tooltip: {
formatter: function (params) {
var startDate = new Date(params.value[1]).format("yyyy-MM-dd HH:mm:ss")
var endDate = new Date(params.value[2]).format("yyyy-MM-dd HH:mm:ss")
var result = params.marker + params.name + ' 修改了'+ params.value[4]+"次</br>"+ startDate+"到"+endDate;
return result;
}
},
legend: {
data:machines,
x: 'left',
y: 'top',
},
grid: {
left:50,
right:50
},
xAxis: {
type:"time",
boundaryGap: false,
min: startTime,
max: endTime,
scale: true,
position:"top",
axisLabel: {
formatter: function (val) {
var date = new Date(val).format("MM-dd HH");
return date;
}
}
},
yAxis: {
type:"value",
inverse: true,
show:false,
scale: true,
max:max,
minInterval: 1,
},
dataZoom:[{
type:"inside",
filter:"empty",
xAxisIndex: 0,
},
{
type: 'slider',
filter:"empty",
xAxisIndex: 0,
},
],
series: series
};
var myChart = echarts.init(document.getElementById("paraDataChart"));
window.onresize = function(){//适应屏幕的变化,重新计算图形
myChart.resize();
}
myChart.setOption(option);
myChart.dispatchAction({
type: 'dataZoom',
// 可选,dataZoom 组件的 index,多个 dataZoom 组件时有用,默认为 0
dataZoomIndex: [0,1,2,3],
// 开始位置的百分比,0 - 100
start: 0,
// 结束位置的百分比,0 - 100
end: 100,
})
myChart.on('datazoom', function (params) {//忘了有什么用。。
var opt = myChart.getOption();
var dz = opt.dataZoom[0];
duration = dz.endValue - dz.startValue;
});
myChart.on('legendselectchanged',function(params){//点击图例可以隐藏对应的机器色块
console.log(params);
var selected = params.selected;
var opt = myChart.getOption();
opt.series[0].data = $.grep(data,function(n,i){
if(selected[n.name]){
return true;
}else{
return false;
}
});
myChart.setOption(opt,true);
});
myChart.on('click',function(params){//色块点击事件
console.log(params.data);
});
});
}
下面是echart自定义模式的核心代码,其实就是烂大街的东西,直接官网看一下,然后自己尝试一下
function renderItem(params, api) {
var categoryIndex = api.value(0);
var count = api.value(4);
var start = api.coord([api.value(1), 0]);//
var end = api.coord([api.value(2), 0]);//
var yheight = api.size([0, categoryIndex])[1];//色块y轴起始
var height = api.size([0, getMax(count)])[1];//色块长度
var rectShape = echarts.graphic.clipRectByRect({
x: start[0],
y: start[1] + yheight,
width: end[0] - start[0],
height: height
}, {
x: params.coordSys.x,
y: params.coordSys.y,
width: params.coordSys.width,
height: params.coordSys.height
});
var style = api.style();
var renderItem = rectShape && {
type: 'rect',
shape: rectShape,
style: style
};
return renderItem;
}
完