echarts本身并不支持直方图,在官方的示例文档上,直方图是利用自定义图表实现的,之前在项目中的实现也是直接使用了官方示例的代码。
在今年3、4月份疫情隔离居家办公的时候,我们的产品经理,说我们的直方图的缩放只能针对x轴进行缩放,不能放大y轴,这不简单,我把y轴的缩放直接放开!
结果人傻了,看下官方示例的缩放效果
看来只能自己动手diy了。
首先要理解直方图的renderItem属性是如何渲染出直方图的,这是整体的series
// 正常的直方图的series
const tempseries = {
type: 'custom',
renderItem: function (params: any, api: any) {
let yValue = api.value(2);
// console.log(yValue,'yValue');
let start = api.coord([api.value(0), yValue]);
let size = api.size([(api.value(1) as number) - (api.value(0) as number), yValue]) as number[];
// console.log(start,'start');
// console.log(size,'size');
const style = api.style();
return {
type: 'rect',
shape: {
x: start[0],
y: start[1],
width: size[0],
height: size[1],
},
style,
};
},
label: {
show: false,
position: 'top',
},
dimensions: ['from', 'to', 'count'],
encode: {
x: [0, 1],
y: 2,
tooltip: [0, 1, 2],
itemName: 3,
},
markLine: {
symbol: 'none',
silent: false,
precision: 100,
label: {
position: 'insideEndTop',
silent: true,
},
tooltip: {
formatter: '{c}',
},
data: marklineData,
},
name: key,
yAxisIndex: 0,
data: histogramData.map((item: any, index: number) => {
return {
value: [item[1], item[2], histogramDisplayLog ? Math.log10(item[3]) : item[3], item[0]],
// itemStyle: {
// color: colorList[index],
// },
};
}),
};
series.push(tempseries);
首先看一下data里的数据,这里的histogramLog是因为项目里有转log的需求,若正常,将三元表达式histogramDisplayLog ? Math.log10(item[3]) : item[3] 替换成 item[3]即可
data: histogramData.map((item: any, index: number) => {
return {
value: [item[1], item[2], histogramDisplayLog ? Math.log10(item[3]) : item[3], item[0]],
// itemStyle: {
// color: colorList[index],
// },
};
}),
是一个对象数组,里面有字段value,value是一个长度为4的数组,再看data数组与renderItem的对应关系:
renderItem: function (params: any, api: any) {
let yValue = api.value(2);
let start = api.coord([api.value(0), yValue]);
let size = api.size([(api.value(1) as number) - (api.value(0) as number), yValue]) as number[];
const style = api.style();
return {
type: 'rect',
shape: {
x: start[0],
y: start[1],
width: size[0],
height: size[1],
},
style,
};
},
dimensions: ['from', 'to', 'count'],
encode: {
x: [0, 1],
y: 2,
tooltip: [0, 1, 2],
itemName: 3,
},
可知,依据官方示例的格式,我们的每个直方图柱子的data的value数组可以这么理解
[x轴的开始位置坐标,x轴的结束位置坐标,y轴的值,柱子的名称]
其中shape下的x、y、width、height分别是这样的对应每个柱子
PS:(文档上更详细。。。。。。)
知道了所有的画法,就可以着手开始重写缩放后的整个series了
首先获取缩放的范围的信息
if (typeof params.batch[0].startValue === 'number') {
const startX = params.batch[0].startValue;
const endX = params.batch[0].endValue;
const startY = params.batch[1].startValue;
const endY = params.batch[1].endValue;
重写的关键的两个个点:
1、data
x轴层面只取完全在缩放x范围内的柱子。即只有x的开始位置坐标大于startX且x的结束位置坐标小于startX的柱子才可以入选我们的data
data: histogramData.map((item: any, index: number) => {
if (
(histogramDisplayLog ? Math.log10(item[3]) : item[3]) > startY &&
item[1] > startX &&
item[2] < endX
)
return {
value: [item[1], item[2], histogramDisplayLog ? Math.log10(item[3]) : item[3], item[0]],
// itemStyle: {
// color: colorList[index],
// },
};
}),
2、renderItem
首先是 return的shape中的x和y
分类讨论y,
①若框选的y的较大值endY大于我们柱子的值,则取柱子的值即可,缩放之后,还是柱子高度距离顶部有距离
②若框选的y的较大值endY小于我们柱子的值,则取endY,即舍弃掉柱子超出框选范围的部分,该柱子在高度上填充满整个高度(产品经理的需求。。。。。。)
let yValue = api.value(2) > endY ? endY : api.value(2);
x还是之前的每个柱子的x的起始位置坐标
let start = api.coord([api.value(0), yValue]);
然后是柱子的height和width
①width不变还是x的结束位置坐标减去x的开始位置坐标
②height应为柱子的y的值(yValue)减去缩放的y的开始位置(startY)
let size = api.size([
(api.value(1) as number) - (api.value(0) as number),
bignumberFc.subtract(yValue, startY),
]) as number[];
综合的renderItem应为
renderItem: function (params: any, api: any) {
let yValue = api.value(2) > endY ? endY : api.value(2);
let start = api.coord([api.value(0), yValue]);
let size = api.size([
(api.value(1) as number) - (api.value(0) as number),
bignumberFc.subtract(yValue, startY),
]) as number[];
const style = api.style();
return {
type: 'rect',
shape: {
x: start[0],
y: start[1],
width: size[0],
height: size[1],
},
style,
};
},
最后贴上整体的datazoom响应部分的函数(如果是回退到初始状态,需要额外判断特殊处理,echarts的缩放的坑)
myChart.on('datazoom', (params: any) => {
//回退到最初始的状态时
if (params.batch[0].start === 0) {
myChart.setOption(
{
series: [...series],
},
{
replaceMerge: ['series'], // 替换合并series,默认普通合并
},
);
}
// 有startValue值的时候(常规缩放)
else if (typeof params.batch[0].startValue === 'number') {
const startX = params.batch[0].startValue;
const endX = params.batch[0].endValue;
const startY = params.batch[1].startValue;
const endY = params.batch[1].endValue;
const newSeries: any[] = [];
if (data && Object.keys(data).length > 0) {
for (let key in data) {
const histogramData = data[key]['data'];
const tempseries = {
type: 'custom',
renderItem: function (params: any, api: any) {
let yValue = api.value(2) > endY ? endY : api.value(2);
let start = api.coord([api.value(0), yValue]);
let size = api.size([
(api.value(1) as number) - (api.value(0) as number),
bignumberFc.subtract(yValue, startY),
]) as number[];
const style = api.style();
return {
type: 'rect',
shape: {
x: start[0],
y: start[1],
width: size[0],
height: size[1],
},
style,
};
},
label: {
show: false,
position: 'top',
},
dimensions: ['from', 'to', 'count'],
encode: {
x: [0, 1],
y: 2,
tooltip: [0, 1, 2],
itemName: 3,
},
name: key,
data: histogramData.map((item: any, index: number) => {
if (
(histogramDisplayLog ? Math.log10(item[3]) : item[3]) > startY &&
item[1] > startX &&
item[2] < endX
)
return {
value: [item[1], item[2], histogramDisplayLog ? Math.log10(item[3]) : item[3], item[0]],
};
}),
};
newSeries.push(tempseries);
}
}
myChart.setOption(
{
series: [...newSeries],
},
{
replaceMerge: ['series'], // 替换合并series,默认普通合并
},
);
}
});
PS:
项目中数据结构如此,依据个人的实际结构进行组织即可
PS2:data中的的histogramLog是因为项目里有转log的需求,若正常使用,将三元表达式histogramDisplayLog ? Math.log10(item[3]) : item[3] 替换成 item[3]即可
效果展示:
缩放前
缩放动作
缩放后的效果
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。