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

React Highcharts:窗口Resize时调整自定义Tooltip位置

解决Highcharts自定义Tooltip窗口缩放时位置不更新的问题

问题描述

在React项目中使用Highcharts实现了点击定位自定义Tooltip的功能,但窗口大小缩放时,Tooltip位置不会自动适配调整。

解决方案

通过监听Highcharts的resize事件,在窗口缩放时重新计算Tooltip的位置;同时封装位置计算逻辑,复用在所有需要更新Tooltip位置的场景(图表加载、缩放选择、点点击、窗口resize)。

修改后的完整代码

import React, { useState } from 'react';
import Highcharts from 'highcharts';
import HighchartsReact from 'highcharts-react-official';
import sampleData from './output.json';
import { Tooltip } from './components/Tooltip.jsx';
import './App.css';

function App() {
  const [left, setLeft] = useState(0);
  const [showToolTip, toggleToolTip] = useState(true);
  const [timeStamp, setTimeStamp] = useState(null);

  const getColor = (color) => {
    switch (color) {
      case 'NORMAL':
        return '#01a982';
      case 'MISSING':
        return '#757575';
      case 'WARNING':
        return 'blue';
      default:
        return 'blue';
    }
  };

  // 提取时间线数据
  const timelineData = sampleData.data;

  // 格式化Highcharts所需数据,保留原始timestamp用于后续匹配
  const chartData = timelineData.map(entry => ({
    x: new Date(entry.timestamp),
    y: 10, // 固定y值
    color: getColor(entry.status),
    timestamp: entry.timestamp
  }));

  // 封装Tooltip位置更新逻辑,统一处理所有场景
  const updateTooltipPosition = (chart, targetTs = null) => {
    let targetTimestamp = targetTs;
    let targetPoint;

    if (targetTimestamp) {
      // 根据目标timestamp查找对应的数据点
      targetPoint = chart.series[0].points.find(point => point.timestamp === targetTimestamp);
    }

    if (!targetPoint) {
      // 无匹配点时,默认取最后一个数据点
      const lastItem = timelineData[timelineData.length - 1];
      targetTimestamp = lastItem.timestamp;
      targetPoint = chart.series[0].points.find(point => point.timestamp === targetTimestamp);
    }

    if (targetPoint) {
      // 计算点在图表中的实际x坐标(包含plotLeft偏移)
      const newLeft = chart.plotLeft + targetPoint.plotX;
      setLeft(newLeft);
      setTimeStamp(targetTimestamp);
    } else {
      // 极端无数据场景下设置默认位置
      setLeft(chart.plotWidth + 10);
    }
  };

  const options = {
    credits: {
      enabled: false,
    },
    chart: {
      type: 'column',
      height: '140px',
      zoomType: 'x',
      resetZoomButton: {
        position: {
          align: 'right',
          verticalAlign: 'top',
          x: -10,
          y: 10,
        },
        relativeTo: 'chart',
      },
      marginTop: '40',
      events: {
        load: function () {
          updateTooltipPosition(this);
        },
        selection: function (event) {
          const chart = this;
          if (event?.xAxis && event.xAxis.length > 0) {
            // 缩放选择时,取x轴最大值对应的最近数据点
            const maxX = event.xAxis[0].max;
            const targetPoint = chart.series[0].points.find(point => point.x <= maxX);
            updateTooltipPosition(chart, targetPoint?.timestamp);
          } else {
            // 重置缩放时回到最后一个数据点
            updateTooltipPosition(chart);
          }
        },
        resize: function () {
          // 窗口缩放时,根据当前timeStamp重新计算Tooltip位置
          updateTooltipPosition(this, timeStamp);
        }
      },
    },
    title: {
      text: null,
    },
    xAxis: {
      minPadding: 0,
      maxPadding: 0,
      dateTimeLabelFormats: {
        hour: '%I %p',
      },
      type: 'datetime',
      title: {
        text: null,
      },
    },
    yAxis: {
      visible: false,
      title: {
        text: null,
      },
    },
    tooltip: false,
    plotOptions: {
      series: {
        turboThreshold: 2000,
        groupPadding: 0,
        pointPadding: 0,
        showInLegend: false,
        marker: {
          enabled: true,
        },
        cursor: 'pointer',
        point: {
          events: {
            click: function (e) {
              // 点击数据点时更新Tooltip位置
              updateTooltipPosition(e.chart, this.timestamp);
              toggleToolTip(true);
            },
          },
        },
      },
    },
    series: [
      {
        name: '',
        showInLegend: false,
        data: chartData,
        dataLabels: {
          enabled: true,
          formatter: function () {
            return this.point.cause;
          },
        },
      },
    ],
  };

  return (
    <div className="App">
      <div id="container">
        <HighchartsReact highcharts={Highcharts} options={options} />
        <Tooltip left={left} timeStamp={timeStamp} showToolTip={showToolTip} />
      </div>
    </div>
  );
}

export default App;

关键修改点说明

  • 统一位置计算逻辑:封装updateTooltipPosition函数,避免重复代码,保证所有场景下Tooltip位置计算的一致性。
  • 监听窗口resize事件:在Highcharts图表配置中添加resize事件回调,触发时根据当前选中的时间戳重新计算Tooltip位置。
  • 保留原始时间戳:格式化图表数据时为每个点添加timestamp字段,避免因Date对象转换导致的匹配误差。
  • 兼容边界场景:处理无匹配点的极端情况,自动 fallback 到最后一个数据点的位置,保证Tooltip始终有合理显示位置。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 13:45:29