开发过程中遇到一个Echarts相关需求: 给出一组折线图数据,Y轴刻度从0开始,第一刻度为数组最小值,最后一个刻度大于数组最大值。
例:
// 折线图数据
const data = [100000, 3000, 82000, 93002, 90001, 90034, 120090]
最终渲染Y轴刻度分别为:[0, 3000, ... , 130000]
核心要点在于
-
保留Y轴从0开始同时手动设置刻度
-
保持折线图的正常走势
考虑过几个方案,但具体效果都不太理想:
- Y轴改为类目轴,手动计算刻度。
- 设置Y轴min、max属性,手动markline一条y=0的直线。
搜索后看到两个比较可取的解决方法:
ECharts自定义Y轴间隔刻度,不均分配 - CSDN博客
记一种echarts绘制不均匀刻度的方法 - 掘金 (juejin.cn)
两种方法思路基本都是将原始数据处理计算映射成一套新数据,实际Y轴刻度axisLabel和tooltip则显示原始数据,以下是实现。
先计算刻度并得到最终实际显示的刻度数组:
var data = [100000,3000, 82000, 93002, 90001, 90034, 120090];
var data2 = [120345,50032,134205,10254,9042,11200,16600];
var max = Math.max(...data,...data2);
var min = Math.min(...data,...data2);
// 添加了0和data中最大值,只计算中间的间隔
var interval = parseInt((max - min) / (intervalIndex - 2));
// 最终显示的刻度数组
var tickArr = [0];
for (var i = 0; i <= intervalIndex; i++) {
tickArr.push(min + i * interval);
}
遍历处理各组数据:
series.forEach((seriesItem)=>{
const data = seriesItem.data;
// 保留原始值
seriesItem.originData = data;
let showData = [];
for (var i = 0; i < data.length; i++) {
// 1.寻找在数据间隔里小于emailData的最大值
const min_v = Math.max(...tickArr.filter((v) => v <= data[i]));
// 2.寻找在数据间隔里大于emailData的最小值
const max_v = Math.min(...tickArr.filter((v) => v > data[i]));
// 3.寻找 min_v 所在的下标
const index = tickArr.findIndex((v) => v === min_v);
// 4.计算该amount在y轴上应该展示的位置
const y_value = ((data[i] - min_v) / (max_v - min_v)) * 10 + index * 10;
showData[i] = parseInt(y_value);// 取整
}
seriesItem.data = showData;
})
图表配置option中将显示值还原为原始数据:
// 提示框,按默认格式显示
tooltip: {
trigger: 'axis',
formatter: (v) => {
// 界面显示原数值
let str = ''
v.forEach((e,i)=>{
str += `<div>${v[i].marker}${v[i].seriesName}:${data[v[i].dataIndex]}</div>`
})
return `<div>
<div>${v[0].name}</div>
${str}
</div>`;
}
},
// Y轴label
yAxis: {
axisLabel: {
formatter: (v, i) => {
v = tickArr[i];
return v;
}
},
},
效果:
值得注意的是,Y轴axisLabel和tooltip中的显示都能还原为原始值,但坐标轴指示器axisPointer所显示的值则没有办法替换,如果配置了axisPointer会露馅。
完整代码: 扩写自Echarts官网折线图示例,复制到示例编辑框可直接查看效果。
// demo给出了两组长度相同的数据,更多折线数可参考上文遍历处理
var data = [100000,3000, 82000, 93002, 90001, 90034, 120090];
var data2 = [120345,50032,134205,10254,9042,11200,16600];
// x轴
var xData = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
// 定义一个常数用于计算
const n = 100;
// 刻度数量
const intervalIndex = 7;
var max = Math.max(...data,...data2);
var min = Math.min(...data,...data2);
// 添加了0和data中最大值,只计算中间的间隔
var interval = parseInt((max - min) / (intervalIndex - 2));
// 最终显示的刻度数组
var tickArr = [0];
for (var i = 0; i <= intervalIndex; i++) {
tickArr.push(min + i * interval);
}
var showData = [];
for (var i = 0; i < data.length; i++) {
// 1.寻找在数据间隔里小于emailData的最大值
const min_v = Math.max(...tickArr.filter((v) => v <= data[i]));
// 2.寻找在数据间隔里大于emailData的最小值
const max_v = Math.min(...tickArr.filter((v) => v > data[i]));
// 3.寻找 min_v 所在的下标
const index = tickArr.findIndex((v) => v === min_v);
// 4.计算该amount在y轴上应该展示的位置
const y_value = ((data[i] - min_v) / (max_v - min_v)) * n + index * n;
// 取整方便查看和对比
showData[i] = parseInt(y_value);
}
var showData2 = [];
for (var i = 0; i < data2.length; i++) {
const min_v = Math.max(...tickArr.filter((v) => v <= data[i]));
const max_v = Math.min(...tickArr.filter((v) => v > data[i]));
const index = tickArr.findIndex((v) => v === min_v);
const y_value = ((data2[i] - min_v) / (max_v - min_v)) * n + index * n;
showData2[i] = parseInt(y_value);
}
option = {
tooltip: {
trigger: 'axis',
formatter: (v) => {
// 界面显示原数值
let str = ''
v.forEach((e,i)=>{
str += `<div>${v[i].marker}${v[i].seriesName}:${data[v[i].dataIndex]}</div>`
})
return `<div>
<div>${v[0].name}</div>
${str}
</div>`;
}
},
xAxis: {
type: 'category',
data: xData
},
yAxis: {
axisLabel: {
formatter: (v, i) => {
return tickArr[i];
}
},
max: (value) => {
// Y轴上限刻度取一个定值,这里给到1.05
return value.max * 1.05;
}
},
series: [
{
data: showData,
type: 'line',
smooth: true,
name:'data1'
},
{
data: showData2,
type: 'line',
smooth: true,
name:'data2'
}
]
};
代码存在一定的数据兼容问题,如负数和多组差异过大的数据,暂时匆忙记下思路和基本操作以便参考。如有更好的实现方法请在评论提出。