记录:echarts tooltip内容过多时,会导致部分内容遮挡

问题:echarts tooltip内容过多时,会导致部分内容遮挡,如下图

优化:

  1. 数据不多时则保持原来方式展示
  1. 数据过多时使用滚动条+一行展示两个的方式来展示

优化效果vs:

  1. 优化前
js 复制代码
const initEcharts = (items, yItems, legends) => {
  var chartDom = document.getElementById("main");

  if (!chartDom) {
    console.error("chart dom not found");
    return;
  }


  myChart = echarts.init(chartDom);
  var option;

  // 显示所有图例
  option = {
    tooltip: {
      trigger: "axis",
    },
    legend: {
      data: legends,
      selectedMode: true, // 启用图例的多选模式
      // left: "left",
      // width: "99%", // 设置图例总宽度
      right: "240px", // 设置右边距
    },
    grid: {
      left: "3%",
      right: "4%",
      bottom: "3%",
      containLabel: true,
      top: "20%", // 设置图表与上方图例的距离
    },
    toolbox: {
      show: true,
      feature: {
        dataZoom: {
          yAxisIndex: "none",
        },
        dataView: { readOnly: false },
        magicType: { type: ["line", "bar"] },
        restore: {},
        saveAsImage: {},
        myTool1: {
          show: true,
          title: "Hide/Show All Legends",
          icon: "path://M20,20 H80 V80 H20 Z M30,30 H90 V90 H30 Z M40,60 L55,75 L80,50",
          onclick: function () {
            toggleLegendVisibility();
          },
        },
      },
    },
    xAxis: {
      type: "category",
      boundaryGap: false,
      data: items,
    },
    yAxis: {
      type: "value",
    },
    series: yItems,
  };

  myChart.clear();
  option && myChart.setOption(option);
};
  1. 一行展示两个
js 复制代码
const initEcharts = (items, yItems, legends) => {
  var chartDom = document.getElementById("main");

  if (!chartDom) {
    console.error("chart dom not found");
    return;
  }


  myChart = echarts.init(chartDom);
  var option;

  // 显示所有图例
  option = {
    tooltip: {
      trigger: "axis",
      appendToBody: true, // 将 tooltip 附加到 body,避免被容器边界截断
      confine: false, // appendToBody 为 true 时,confine 应设为 false
      enterable: true, // 允许鼠标进入 tooltip,方便滚动查看
      extraCssText: "max-height: 400px; overflow-y: auto; min-width: 400px;", // 设置最大高度并添加滚动条,设置最小宽度以容纳两列
      axisPointer: {
        type: "line",
      },
      // 自定义 formatter,一行显示两个期刊信息
      formatter: function (params: any) {
        if (!params || params.length === 0) return "";
        
        // 获取 x 轴的值(日期)
        const date = params[0].axisValue;
        let result = `<div style="font-weight: bold; margin-bottom: 8px; padding-bottom: 4px; border-bottom: 1px solid #eee;">${date}</div>`;
        
        // 将数据按两列显示
        const items: string[] = [];
        for (let i = 0; i < params.length; i += 2) {
          const param = params[i];
          const marker = param.marker || "";
          const name = param.seriesName || "";
          const value = param.value !== null && param.value !== undefined ? param.value : 0;
          
          // 每行显示两个期刊,使用 flex 布局
          const nextParam = params[i + 1];
          if (nextParam) {
            const nextMarker = nextParam.marker || "";
            const nextName = nextParam.seriesName || "";
            const nextValue = nextParam.value !== null && nextParam.value !== undefined ? nextParam.value : 0;
            items.push(
              `<div style="display: flex; margin-bottom: 4px; line-height: 1.5;">` +
              `<span style="flex: 1; min-width: 0; padding-right: 10px;">${marker} ${name}: ${value}</span>` +
              `<span style="flex: 1; min-width: 0; padding-left: 10px; border-left: 1px solid #eee;">${nextMarker} ${nextName}: ${nextValue}</span>` +
              `</div>`
            );
          } else {
            // 最后一个,单独显示
            items.push(
              `<div style="margin-bottom: 4px; line-height: 1.5;">${marker} ${name}: ${value}</div>`
            );
          }
        }
        
        result += items.join("");
        return result;
      },
      // 自定义位置,确保 tooltip 在可视区域内
      position: function (point: number[], _params: any, _dom: any, _rect: any, size: any) {
        // point: 鼠标位置
        // size: tooltip 的大小
        const [x, y] = point;
        const viewWidth = size.viewSize[0];
        const viewHeight = size.viewSize[1];
        const boxWidth = size.contentSize[0];
        const boxHeight = size.contentSize[1];
        
        // 默认位置在鼠标右侧
        let posX = x + 10;
        let posY = y;
        
        // 如果右侧空间不足,显示在左侧
        if (posX + boxWidth > viewWidth) {
          posX = x - boxWidth - 10;
        }
        
        // 如果下方空间不足,向上调整
        if (posY + boxHeight > viewHeight) {
          posY = viewHeight - boxHeight - 10;
        }
        
        // 确保不超出上边界
        if (posY < 10) {
          posY = 10;
        }
        
        return [posX, posY];
      },
    },
    legend: {
      data: legends,
      selectedMode: true, // 启用图例的多选模式
      // left: "left",
      // width: "99%", // 设置图例总宽度
      right: "240px", // 设置右边距
    },
    grid: {
      left: "3%",
      right: "4%",
      bottom: "3%",
      containLabel: true,
      top: "20%", // 设置图表与上方图例的距离
    },
    toolbox: {
      show: true,
      feature: {
        dataZoom: {
          yAxisIndex: "none",
        },
        dataView: { readOnly: false },
        magicType: { type: ["line", "bar"] },
        restore: {},
        saveAsImage: {},
        myTool1: {
          show: true,
          title: "Hide/Show All Legends",
          icon: "path://M20,20 H80 V80 H20 Z M30,30 H90 V90 H30 Z M40,60 L55,75 L80,50",
          onclick: function () {
            toggleLegendVisibility();
          },
        },
      },
    },
    xAxis: {
      type: "category",
      boundaryGap: false,
      data: items,
    },
    yAxis: {
      type: "value",
    },
    series: yItems,
  };

  myChart.clear();
  option && myChart.setOption(option);
};
  1. 当期刊超过20条时,使用自定义 formatter 和滚动条; 当期刊不超过20条时,不设置 formatter,使用 ECharts 默认的显示方式(没有最小宽度,没有滚动条)
js 复制代码
const initEcharts = (items, yItems, legends) => {
  var chartDom = document.getElementById("main");

  if (!chartDom) {
    console.error("chart dom not found");
    return;
  }


  myChart = echarts.init(chartDom);
  var option;

  // 根据期刊数量决定 tooltip 配置
  const tooltipConfig: any = {
    trigger: "axis",
    appendToBody: true, // 将 tooltip 附加到 body,避免被容器边界截断
    confine: false, // appendToBody 为 true 时,confine 应设为 false
    enterable: true, // 允许鼠标进入 tooltip,方便滚动查看
    axisPointer: {
      type: "line",
    },
    // 自定义位置,确保 tooltip 在可视区域内
    position: function (point: number[], _params: any, _dom: any, _rect: any, size: any) {
      // point: 鼠标位置
      // size: tooltip 的大小
      const [x, y] = point;
      const viewWidth = size.viewSize[0];
      const viewHeight = size.viewSize[1];
      const boxWidth = size.contentSize[0];
      const boxHeight = size.contentSize[1];
      
      // 默认位置在鼠标右侧
      let posX = x + 10;
      let posY = y;
      
      // 如果右侧空间不足,显示在左侧
      if (posX + boxWidth > viewWidth) {
        posX = x - boxWidth - 10;
      }
      
      // 如果下方空间不足,向上调整
      if (posY + boxHeight > viewHeight) {
        posY = viewHeight - boxHeight - 10;
      }
      
      // 确保不超出上边界
      if (posY < 10) {
        posY = 10;
      }
      
      return [posX, posY];
    },
  };

  // 当期刊超过20条时,使用自定义 formatter 和滚动条
  if (legends && legends.length > 20) {
    tooltipConfig.extraCssText = "max-height: 400px; overflow-y: auto; min-width: 400px;"; // 设置最大高度并添加滚动条,设置最小宽度以容纳两列
    tooltipConfig.formatter = function (params: any) {
      if (!params || params.length === 0) return "";
      
      // 获取 x 轴的值(日期)
      const date = params[0].axisValue;
      let result = `<div style="font-weight: bold; margin-bottom: 8px; padding-bottom: 4px; border-bottom: 1px solid #eee;">${date}</div>`;
      
      // 两列显示
      const items: string[] = [];
      for (let i = 0; i < params.length; i += 2) {
        const param = params[i];
        const marker = param.marker || "";
        const name = param.seriesName || "";
        const value = param.value !== null && param.value !== undefined ? param.value : 0;
        
        // 每行显示两个期刊,使用 flex 布局
        const nextParam = params[i + 1];
        if (nextParam) {
          const nextMarker = nextParam.marker || "";
          const nextName = nextParam.seriesName || "";
          const nextValue = nextParam.value !== null && nextParam.value !== undefined ? nextParam.value : 0;
          items.push(
            `<div style="display: flex; margin-bottom: 4px; line-height: 1.5;">` +
            `<span style="flex: 1; min-width: 0; padding-right: 10px;">${marker} ${name}: ${value}</span>` +
            `<span style="flex: 1; min-width: 0; padding-left: 10px; border-left: 1px solid #eee;">${nextMarker} ${nextName}: ${nextValue}</span>` +
            `</div>`
          );
        } else {
          // 最后一个,单独显示
          items.push(
            `<div style="margin-bottom: 4px; line-height: 1.5;">${marker} ${name}: ${value}</div>`
          );
        }
      }
      
      result += items.join("");
      return result;
    };
  }
  // 当期刊不超过20条时,不设置 formatter,使用 ECharts 默认的显示方式(没有最小宽度,没有滚动条)

  // 显示所有图例
  option = {
    tooltip: tooltipConfig,
    legend: {
      data: legends,
      selectedMode: true, // 启用图例的多选模式
      // left: "left",
      // width: "99%", // 设置图例总宽度
      right: "240px", // 设置右边距
    },
    grid: {
      left: "3%",
      right: "4%",
      bottom: "3%",
      containLabel: true,
      top: "20%", // 设置图表与上方图例的距离
    },
    toolbox: {
      show: true,
      feature: {
        dataZoom: {
          yAxisIndex: "none",
        },
        dataView: { readOnly: false },
        magicType: { type: ["line", "bar"] },
        restore: {},
        saveAsImage: {},
        myTool1: {
          show: true,
          title: "Hide/Show All Legends",
          icon: "path://M20,20 H80 V80 H20 Z M30,30 H90 V90 H30 Z M40,60 L55,75 L80,50",
          onclick: function () {
            toggleLegendVisibility();
          },
        },
      },
    },
    xAxis: {
      type: "category",
      boundaryGap: false,
      data: items,
    },
    yAxis: {
      type: "value",
    },
    series: yItems,
  };

  myChart.clear();
  option && myChart.setOption(option);
};
相关推荐
小满zs2 小时前
Next.js第四章(路由导航)
前端
进击的野人2 小时前
深入理解 CSS4 新特性:CSS 变量
前端·css
DevUI团队2 小时前
🚀 MateChat发布V1.10.0版本,支持附件上传及体验问题修复,欢迎体验~
前端·vue.js·人工智能
用户345848285052 小时前
Vue是怎么实现双向绑定的
前端
彩虹下面2 小时前
手把手带你阅读vue2源码
前端·javascript·vue.js
华洛2 小时前
经验贴:Agent实战落地踩坑六大经验教训,保姆教程。
前端·javascript·产品
luckyzlb2 小时前
03-node.js & webpack
前端·webpack·node.js
左耳咚2 小时前
如何解析 zip 文件
前端·javascript·面试
程序员小寒2 小时前
前端高频面试题之Vue(初、中级篇)
前端·javascript·vue.js