You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ECharts折线图中对特定Y轴区间进行非线性缩放(偏斜显示)

如何在ECharts折线图中对特定Y轴区间进行非线性缩放(偏斜显示)

我完全理解你想让Y轴某段区间占更大绘图空间的需求,这种非线性缩放的效果在ECharts里其实可以通过自定义Y值的视觉映射函数来实现,不用太复杂的操作,我结合你提供的代码给你一步步说明~

核心思路

我们的核心思路是:把原始的Y轴数值,按照我们想要的比例映射到一个新的“视觉数值”上——让需要压缩的区间(比如你的0-40)对应到更小的视觉范围,需要拉伸的区间(40-50)对应到更大的视觉范围。然后让ECharts用这个视觉数值来绘图,同时把Y轴的刻度标签显示回原始数值,这样用户看到的就是“区间被拉伸/压缩”的效果,但刻度还是原始的真实值。

具体实现步骤

首先我们先写好原始值↔视觉值的双向转换函数,这是实现的核心:

// 原始Y值转视觉值:0-40对应视觉0-33(占1/3高度),40-50对应33-100(占2/3高度)
function yToVisual(y) {
  if (y <= 40) {
    return (y / 40) * 33;
  } else {
    return 33 + ((y - 40) / 10) * 67;
  }
}

// 视觉值转原始Y值,用于Y轴刻度显示
function visualToY(v) {
  if (v <= 33) {
    return Math.round((v / 33) * 40);
  } else {
    return 40 + Math.round(((v - 33) / 67) * 10);
  }
}

接下来,我们修改你的ECharts配置,把这个映射逻辑加进去:

1. 处理数据:把原始Y值转换成视觉值

原来的eChartsDataValues是直接用原始Y值,现在我们换成转换后的视觉值:

const eChartsDataValues = dataValues.map((dv) => [dv.date, yToVisual(dv.value)]);

2. 配置Y轴:让刻度显示回原始值

修改yAxis的配置,让ECharts用视觉值绘图,但刻度标签显示原始值,同时设置关键的刻度点保证准确性:

yAxis: {
  min: 0,
  max: 100, // 视觉值的范围设为0-100,对应整个图表高度
  axisLabel: {
    // 把视觉值转回原始值作为刻度标签
    formatter: function(v) {
      return visualToY(v);
    }
  },
  axisTick: {
    alignWithLabel: true
  },
  // 自定义刻度点,对应原始数据的关键节点,让刻度更直观
  data: [
    yToVisual(0),
    yToVisual(10),
    yToVisual(20),
    yToVisual(30),
    yToVisual(40),
    yToVisual(45),
    yToVisual(50)
  ]
},

3. 适配MarkArea:用视觉值设置区间

因为现在Y轴用的是视觉值,所以MarkArea的Y轴区间也要用转换后的数值:

markArea: {
  silent: true,
  emphasis: {
    disabled: true,
  },
  label: {
    fontSize: 12,
    textBorderColor: "transparent",
    position: "insideBottomLeft",
  },
  data: [
    [
      {
        name: "This is mathematically a shorter range (40-50), but it should take up the majority of space on the graph",
        yAxis: yToVisual(40),
        itemStyle: {
          color: "red",
        },
      },
      {
        yAxis: yToVisual(50),
      },
    ],
    [
      {
        name: "This is mathematically a bigger range (0-40) but should take up a smaller section of the graph",
        yAxis: yToVisual(0),
        itemStyle: {
          color: "green",
        },
      },
      {
        yAxis: yToVisual(40),
      },
    ],
  ],
},

完整修改后的代码

把所有修改整合后,你的代码会变成这样:

import { format } from "date-fns";
import { createFakeValues } from "../utils";

// 原始Y值转视觉值:0-40对应视觉0-33(占1/3高度),40-50对应33-100(占2/3高度)
function yToVisual(y) {
  if (y <= 40) {
    return (y / 40) * 33;
  } else {
    return 33 + ((y - 40) / 10) * 67;
  }
}

// 视觉值转原始Y值,用于Y轴刻度显示
function visualToY(v) {
  if (v <= 33) {
    return Math.round((v / 33) * 40);
  } else {
    return 40 + Math.round(((v - 33) / 67) * 10);
  }
}

const dataValues = createFakeValues({
  yValues: [29, 32, 35, 40, 47, 49, 50, 49, 48, 45, 43, 39, 35, 30, 27, 25, 24],
  startDate: new Date("2024-12-01T18:27:08.199Z"),
  dateDeltaMs: 1800000,
});

const eChartsDataValues = dataValues.map((dv) => [dv.date, yToVisual(dv.value)]);

export const eChartsOptions = {
  dataset: [
    {
      source: eChartsDataValues,
      dimensions: ["timestamp", "value"],
    },
  ],
  xAxis: {
    type: "time",
  },
  yAxis: {
    min: 0,
    max: 100,
    axisLabel: {
      formatter: function(v) {
        return visualToY(v);
      }
    },
    axisTick: {
      alignWithLabel: true
    },
    data: [
      yToVisual(0),
      yToVisual(10),
      yToVisual(20),
      yToVisual(30),
      yToVisual(40),
      yToVisual(45),
      yToVisual(50)
    ]
  },
  series: [
    {
      name: "Y Level",
      type: "line",
      smooth: true,
      datasetIndex: 0,
      showSymbol: false,
      encode: {
        x: "timestamp",
        y: "value",
      },
      markArea: {
        silent: true,
        emphasis: {
          disabled: true,
        },
        label: {
          fontSize: 12,
          textBorderColor: "transparent",
          position: "insideBottomLeft",
        },
        data: [
          [
            {
              name: "This is mathematically a shorter range (40-50), but it should take up the majority of space on the graph",
              yAxis: yToVisual(40),
              itemStyle: {
                color: "red",
              },
            },
            {
              yAxis: yToVisual(50),
            },
          ],
          [
            {
              name: "This is mathematically a bigger range (0-40) but should take up a smaller section of the graph",
              yAxis: yToVisual(0),
              itemStyle: {
                color: "green",
              },
            },
            {
              yAxis: yToVisual(40),
            },
          ],
        ],
      },
    },
  ],
};

适配其他区间比例

如果你之后需要调整区间比例(比如你提到的0-70占1/3,70-100占2/3),只需要修改转换函数的逻辑即可:

function yToVisual(y) {
  if (y <= 70) {
    return (y / 70) * 33.33; // 0-70对应0-33.33(1/3高度)
  } else {
    return 33.33 + ((y - 70) / 30) * 66.67; //70-100对应33.33-100(2/3高度)
  }
}

function visualToY(v) {
  if (v <= 33.33) {
    return Math.round((v / 33.33) * 70);
  } else {
    return 70 + Math.round(((v - 33.33) / 66.67) * 30);
  }
}

这样就能轻松实现任意区间的拉伸/压缩需求啦~

备注:内容来源于stack exchange,提问作者dwat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 13:33:09