最近项目组接到了许多对图表有特殊要求的需求,比如今天说的这个呈现光纤的中断时长分布的需求:光纤的中断大部分落在 30 分钟之内,但偶尔遇到特殊情况时,会出现中断一两天甚至更长的时间,如果把这些时长分布放在一个刻度均匀的数轴上,势必造成大部分的指标(此处为柱状图)特别短的问题,因此,希望建立一个刻度一开始均匀分布,比如 10min、20min, 到 60min 之后立刻升为 2hour、1day 这样的需求。
一开始拿到这个任务,我的第一反应,是查找 Echarts 的配置项手册,希望通过在 yAxis 上做手脚来解决问题。然鹅事情总是没有想象的顺利,echart 对 y 轴对配置只能指定几种 type:‘category’、‘value’和‘log’,虽然配置为 log 对数轴也可以解决较大值对较小值对影响的问题,但当值较小时也无法通过长度区分开,不够完美。
庆幸的是,在 echarts 的 GitHub 官网上,pissang 大大给出了一个思路:
深受启发!为什么一定要依赖 Echarts 本身给予解决方案呢?完全可以自己构造一个新的分布呀!
不多说,开始码:
第一步:利用 yAixs.axisLabel.formatter 伪造一个不均匀坐标轴
先给大家看看具体的代码:
// 利用 formatter 将 y 轴上本来为 70(min),80(min) 的点强制改为 2hour 和 1day
formatter:function(value) {
let item=”;
if(value==70){
item=’2hour’
}else if(value===80){
item=’1Day’
}else{
item=value+’ min’
}
return item
}
第二步:将数据映射到一个特定的分布
这句话的意思,其实就是自己构造一个函数,将原始数据里较大的数值转换成一个小数值
我是这样做的:
// 模拟数据,其中的 200、150 是应该落在 2hour 和 1day 之间,所以映射后的数据应该落在 70min 到 80min 之间
let data = [10, 15, 4, 20, 200, 150, 19,70,1441];
function formatData(arr){// 自己构造一个用来映射 data 到均匀数轴上的方法
for(let i=0;i<arr.length;i++){
if(arr[i]>60&&arr[i]<=120){
let percent1=(arr[i]-60)/120;
arr[i]=percent1*10+60;
}else if(arr[i]>120&&arr[i]<1440){
let percent2 =(arr[i]-120)/1440;
console.log(percent2);
arr[i]=percent2*10+70;
}
}
return arr;
}
上面这个 formatData,其实就是对 data 数组里超过 60 的数据进行改造,如果这个数据超过了 60min 且小于 120min(2hour),就按照这个数据在 60 到 120 段应该有的比例,映射到 60 到 70 段里,而超过 2hour 且小于 1day 的数据,则同样按这个方法,计算映射到 70 到 80 段里
这样一来,任务已经基本完成啦!
第三步:将数据反映射为原来的值
但是现在在图表里,我们拿到的是映射后的数据,如果此时的 tooltip 是开放的,那么用户在 tooltip 里读到的数据就不是原来的 200min,150min 这种的了,而变成了一个 70min 到 80min 之间的较小数据。怎么办?只能在每个需要展示数据的地方,严防死守,将这几条特殊的数据反计算回去咯!
tooltip:{
formatter:function(params) {// 由于在 tooltip 里需要展示原始的数据,所以要把映射后的数据反计算回去
let str=params.name+’:’+params.value;
if(params.value>=60&¶ms.value<70){
let percent = (params.value-60)/10
let value = Math.round(percent*120+60);// 注意此处的 percent 已经是个浮点数了,所以得到 value 之前要用四舍五入取整才行
str=params.name+’ : ‘+value;
}else if(params.value>70&¶ms.value<80){
let percent = (params.value-70)/10;
let value =Math.round(percent*1440+120);
str=params.name+’ : ‘+value;
}
return ‘<div style=”width:70px;height:50px;display:flex;align-items:center”>\
<span style=”background-color:#D53A35;width:15px;height:15px;display:inline-block;border-radius:50%”></span>\
<span>’+str+'</span>\
</div>’
}
}
嗯,现在就可以了,接下来上一个效果图炫耀一下:
一切看上去都是那么的完美,其实这种方法当然还是瑕疵的,你能看出来吗:)