Echarts折线图自定义Y轴刻度的简单实现

3,693 阅读2分钟

开发过程中遇到一个Echarts相关需求: 给出一组折线图数据,Y轴刻度从0开始,第一刻度为数组最小值,最后一个刻度大于数组最大值。

例:

// 折线图数据
const data = [100000, 3000, 82000, 93002, 90001, 90034, 120090]
最终渲染Y轴刻度分别为:[0, 3000, ... , 130000]

核心要点在于

  1. 保留Y轴从0开始同时手动设置刻度

  2. 保持折线图的正常走势

考虑过几个方案,但具体效果都不太理想:

  • 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;
      }
    },
  },

效果:

效果图.png

值得注意的是,Y轴axisLabel和tooltip中的显示都能还原为原始值,但坐标轴指示器axisPointer所显示的值则没有办法替换,如果配置了axisPointer会露馅。

完整代码: 扩写自Echarts官网折线图示例,复制到示例编辑框可直接查看效果。

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'
    }
  ]
};

代码存在一定的数据兼容问题,如负数和多组差异过大的数据,暂时匆忙记下思路和基本操作以便参考。如有更好的实现方法请在评论提出。