You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

ChartJS:鼠标悬停时显示上下双提示框的实现方法

实现折线图点悬停双提示框的方案

嘿,这个需求其实完全可以搞定!我之前做过类似的交互,核心就是自定义Tooltip的渲染逻辑,不管你用的是哪款主流图表库,都能找到对应的实现方式。下面给你分库拆解具体步骤:

1. 如果你用的是 ECharts

ECharts的Tooltip支持自定义内容,而且可以通过CSS来控制它的位置或者直接生成两个容器。这里给你两种思路:

思路一:自定义Tooltip内容,用HTML生成上下两个提示块

直接在tooltip.formatter里返回包含两个独立区块的HTML,然后用CSS给这两个区块设置上下排列的样式:

option = {
  tooltip: {
    trigger: 'item',
    formatter: function(params) {
      // 拼接上下两个提示框的内容
      return `
        <div class="tooltip-top">
          <p>上方提示:${params.name}</p>
          <p>数值:${params.value}</p>
        </div>
        <div class="tooltip-bottom">
          <p>下方提示:额外信息A</p>
          <p>备注:${params.seriesName}相关数据</p>
        </div>
      `;
    },
    // 关闭默认样式,方便自定义
    extraCssText: 'padding:0; border:none;'
  },
  // 其他图表配置...
};

然后加CSS样式:

.tooltip-top {
  padding: 8px;
  background: #333;
  color: #fff;
  border-radius: 4px 4px 0 0;
}
.tooltip-bottom {
  padding: 8px;
  background: #666;
  color: #fff;
  border-radius: 0 0 4px 4px;
}

思路二:监听鼠标事件,手动创建两个独立的Tooltip容器

如果需要更灵活的位置控制(比如一个跟着点,一个固定在下方),可以监听mouseovermouseout事件:

const myChart = echarts.init(document.getElementById('main'));

myChart.on('mouseover', function(params) {
  if (params.componentType === 'series') {
    // 获取点的屏幕坐标
    const { offsetX, offsetY } = params.event;
    // 创建上方提示框
    const topTooltip = document.createElement('div');
    topTooltip.className = 'custom-tooltip top';
    topTooltip.innerHTML = `<p>上方提示:${params.name}</p>`;
    topTooltip.style.left = `${offsetX}px`;
    topTooltip.style.top = `${offsetY - 60}px`;
    document.body.appendChild(topTooltip);
    
    // 创建下方提示框
    const bottomTooltip = document.createElement('div');
    bottomTooltip.className = 'custom-tooltip bottom';
    bottomTooltip.innerHTML = `<p>下方提示:额外数据</p>`;
    bottomTooltip.style.left = `${offsetX}px`;
    bottomTooltip.style.top = `${offsetY + 20}px`;
    document.body.appendChild(bottomTooltip);
    
    // 存起来方便移除
    params.event.target.tooltips = [topTooltip, bottomTooltip];
  }
});

myChart.on('mouseout', function(params) {
  if (params.componentType === 'series' && params.event.target.tooltips) {
    params.event.target.tooltips.forEach(el => el.remove());
  }
});

对应的CSS:

.custom-tooltip {
  position: absolute;
  padding: 6px;
  background: rgba(0,0,0,0.8);
  color: #fff;
  border-radius: 4px;
  pointer-events: none;
  z-index: 999;
}

2. 如果你用的是 Chart.js

Chart.js可以通过tooltip.custom方法完全自定义Tooltip的渲染,同样可以生成两个提示框:

const chart = new Chart(ctx, {
  type: 'line',
  data: data,
  options: {
    plugins: {
      tooltip: {
        enabled: false, // 关闭默认Tooltip
        custom: function(context) {
          if (!context.tooltip.opacity) return;
          
          const tooltipEl = document.getElementById('custom-tooltip');
          // 如果不存在则创建
          if (!tooltipEl) {
            const container = document.createElement('div');
            container.id = 'custom-tooltip';
            container.innerHTML = `
              <div class="tooltip-top"></div>
              <div class="tooltip-bottom"></div>
            `;
            document.body.appendChild(container);
          }
          
          // 设置位置
          tooltipEl.style.left = `${context.tooltip.x}px`;
          tooltipEl.style.top = `${context.tooltip.y - 80}px`;
          
          // 填充内容
          const dataPoint = context.tooltip.dataPoints[0];
          tooltipEl.querySelector('.tooltip-top').innerHTML = `
            <p>X轴:${dataPoint.label}</p>
            <p>Y轴:${dataPoint.parsed.y}</p>
          `;
          tooltipEl.querySelector('.tooltip-bottom').innerHTML = `
            <p>系列名称:${dataPoint.dataset.label}</p>
            <p>额外信息:自定义内容</p>
          `;
        }
      }
    }
  }
});

CSS样式和ECharts的类似,这里就不重复啦。

3. 如果你用的是 D3.js

D3.js完全是手动控制DOM,实现起来更灵活:

// 假设已经绘制好折线图,获取所有数据点
const points = svg.selectAll('.data-point');

// 创建两个Tooltip容器
const topTooltip = d3.select('body').append('div')
  .attr('class', 'd3-tooltip top')
  .style('opacity', 0);
const bottomTooltip = d3.select('body').append('div')
  .attr('class', 'd3-tooltip bottom')
  .style('opacity', 0);

// 监听鼠标悬停
points.on('mouseover', function(event, d) {
  // 获取鼠标位置
  const [x, y] = d3.pointer(event);
  
  topTooltip.transition().duration(200).style('opacity', .9);
  topTooltip.html(`<p>上方提示:${d.x}</p>`)
    .style('left', `${event.pageX}px`)
    .style('top', `${event.pageY - 60}px`);
    
  bottomTooltip.transition().duration(200).style('opacity', .9);
  bottomTooltip.html(`<p>下方提示:${d.y}</p>`)
    .style('left', `${event.pageX}px`)
    .style('top', `${event.pageY + 20}px`);
})
.on('mouseout', function() {
  topTooltip.transition().duration(500).style('opacity', 0);
  bottomTooltip.transition().duration(500).style('opacity', 0);
});

核心思路其实就是摆脱默认Tooltip的限制,自己控制HTML内容或者DOM元素,不管用哪个库,都能通过自定义渲染或者事件监听来实现上下双提示框的效果。如果你用的是其他小众库,也可以参考这个思路——找到Tooltip的自定义入口,或者直接监听鼠标事件手动创建容器。

内容的提问来源于stack exchange,提问作者Univers3

火山引擎 最新活动