TypeScript React中Highstocks悬停时上方标签同步显示各序列值
Highstocks悬停同步外部标签数值实现方案(React+TS)
核心逻辑直接复用Highstocks内置的点位匹配能力,不需要手动计算坐标映射,就能实现光标悬停时上方独立div同步显示对应序列数值、垂直虚线标识光标位置的效果。
实现步骤
- 第一步:调整基础图表配置
先关闭Highstocks默认内置的悬浮提示框,避免和自定义外部标签冲突,同时开启垂直方向十字准线(即参考效果里的位置虚线),配置示例:const baseChartOptions: Highcharts.Options = { tooltip: { enabled: false, crosshairs: [ { // 垂直十字线样式 color: '#888', dashStyle: 'Dash', width: 1 }, false // 关闭水平十字线 ] }, // 其余原有配置(xAxis、yAxis、series等)保持不变 } - 第二步:绑定鼠标事件同步数值
借助React的ref存储图表实例,在图表加载完成后给容器绑定鼠标移动、移出事件,通过Highstocks内置的坐标转换、点位搜索方法,直接拿到当前光标位置对齐的所有序列的Y值,同步更新外部div的状态。完整组件示例:import { useRef, useState } from 'react'; import Highcharts from 'highcharts/highstock'; import HighchartsReact from 'highcharts-react-official'; import { throttle } from 'lodash'; // 可选,用于性能优化 // 序列和上方标签的映射关系,和你图表的series顺序一一对应 const SERIES_CONFIG = [ { label: 'Throttle Valve, Posi', key: 'throttle' }, { label: 'Mill Hydr. Unit, Gri', key: 'millHydr' }, { label: 'Water Injection, Pos', key: 'waterInj' } ] as const; export default function ProcessChart() { const chartInsRef = useRef<Highcharts.Chart | null>(null); // 初始化所有标签默认显示为'--' const [labelValues, setLabelValues] = useState<Record<string, string | number>>( SERIES_CONFIG.reduce((acc, cur) => { acc[cur.key] = '--'; return acc; }, {} as Record<string, string>) ); // 鼠标移动时更新数值,加16ms节流适配60帧,避免卡顿 const handleMouseMove = throttle((e: MouseEvent) => { if (!chartInsRef.current) return; // 将原生鼠标事件转换为图表内部坐标,自动对齐X轴最近的有效数据点 const normalizedPoint = chartInsRef.current.pointer.normalize(e); // 遍历所有序列,搜索当前坐标下对应的点位 const currentPoints = chartInsRef.current.series .map(series => series.searchPoint(normalizedPoint, true)) .filter(Boolean); // 更新标签值,数值保留两位小数匹配示例格式 setLabelValues(prev => { const newVal = {...prev}; currentPoints.forEach((point, index) => { const config = SERIES_CONFIG[index]; if (config && point?.y !== undefined) { newVal[config.key] = Number(point.y).toFixed(2); } }); return newVal; }); }, 16); // 鼠标移出图表区域时,所有值重置为'--' const handleMouseLeave = () => { setLabelValues( SERIES_CONFIG.reduce((acc, cur) => { acc[cur.key] = '--'; return acc; }, {} as Record<string, string>) ); }; // 图表加载完成后绑定事件 const chartOptions: Highcharts.Options = { ...baseChartOptions, chart: { events: { load() { chartInsRef.current = this; this.container.addEventListener('mousemove', handleMouseMove); this.container.addEventListener('mouseleave', handleMouseLeave); } } } }; return ( <div className="chart-wrapper"> {/* 图表上方独立标签区域 */} <div className="chart-top-labels" style={{display: 'flex', gap: '2rem', padding: '0.5rem 0'}}> {SERIES_CONFIG.map(item => ( <div key={item.key} className="label-item"> <span>{item.label}: </span> <span className="value">{labelValues[item.key]}</span> </div> ))} </div> {/* Highstocks图表本体 */} <HighchartsReact highcharts={Highcharts} constructorType="stockChart" options={chartOptions} /> </div> ) } - 第三步:格式适配
如果不同序列需要不同的数值格式(比如单位、小数位数),直接在点位遍历的逻辑里单独处理即可,比如注水量数值保留两位、阀门开度保留整数,都可以按需调整。
优化提示
- 图表缩放、平移操作不需要额外适配,
searchPoint方法会自动换算当前可视区域的坐标,不会出现数值错位 - 如果单图表序列超过10条,建议保留节流配置,避免高频触发状态更新导致页面卡顿
- 十字线样式、标签样式可以直接通过css和Highcharts配置调整,完全匹配参考效果的视觉表现
内容的提问来源于stack exchange,提问作者Daniyal Yahya
相关产品推荐
相关产品推荐

