之前完成了一個比較複雜的功能。但是一直沒有時間(懶)把這個代碼分享出來,趁着一個人加班的機會順便寫個博客。下面是實現的具體展示。
這個圖是用來展示機器在一定時間內參數修改的次數。就是說一定時間範圍內,兩次修改的間隔在五分鐘之內,則把他們歸爲同一個色塊。橫座標是統計時間的跨度,縱座標是這段時間內修改的次數。
同樣顏色的色塊代表的是同一臺機器,所以就可以非常直觀明瞭的表現機器修改的次數,如果在比較短的時間內出現大量改動,那麼就會出現非常細長的色塊。我覺得這個功能非常好,雖然需求是領導從別的公司借鑑過來的,不過具體實現都是我自己完成的,花了我兩天時間。
好了,不吹牛了,上代碼。
首先是在後臺統計,統計某時間內機器修改次數。主要就幾個參數,機器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;
}
完