Highcharts实战及讲解|多图表联动与同步图表示例 Demo

Demo目的:演示同一页面多个图表之间如何通过DOM事件与Highcharts API实现联动,悬浮提示框、十字准星同步;X轴缩放范围同步;重置缩放联动

功能概述

本示例实现页面多张图表联动效果

  1. 鼠标悬浮任意一张图表,全部图表同步显示提示框(tooltip)与十字准星(crosshair)
  2. X轴缩放联动:对其中一个图表做X轴框选缩放,其余图表同步缩放X轴范围
  3. 重置缩放:一张图表重置缩放,其余图表同步复原
  4. 数据远程拉取 activity.json,动态循环生成多张独立图表,布局纵向排列。

核心原理:重写 Highcharts 内部指针、点位高亮原型方法,监听 DOM 鼠标事件、坐标轴 setExtremes 事件完成多实例通信。

完整可运行HTML代码:

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>多图表联动同步示例</title>
    <script src="https://code.highcharts.com/highcharts.js"></script>
    <style>
        #container {
            width: 95%;
            margin: 0 auto;
        }
        .chart {
            height: 220px;
        }
    </style>
</head>
<body>
<div id="container"></div>
<script>
/*
  Demo目的:演示同一页面多个图表之间如何通过DOM事件与Highcharts API实现联动
  悬浮提示框、十字准星同步;X轴缩放范围同步;重置缩放联动
*/

/**
 * 监听容器鼠标/触摸事件,实现全部图表tooltip、十字光标同步
 */
['mousemove', 'touchmove', 'touchstart'].forEach(function (eventType) {
    document.getElementById('container').addEventListener(
        eventType,
        function (e) {
            let chart, point, i, event;
            for (i = 0; i < Highcharts.charts.length; i = i + 1) {
                chart = Highcharts.charts[i];
                // 将原生事件转换为图表内部坐标
                event = chart.pointer.normalize(e);
                // 根据坐标查找鼠标悬浮的数据点
                point = chart.series[0].searchPoint(event, true);
                if (point) {
                    point.highlight(e);
                }
            }
        }
    );
});

/**
 * 覆盖原生reset方法,禁止鼠标移出后自动隐藏tooltip与十字准星
 */
Highcharts.Pointer.prototype.reset = function () {
    return undefined;
};

/**
 * 扩展Point原型:高亮点位,显示悬浮标记、提示框、十字准星
 */
Highcharts.Point.prototype.highlight = function (event) {
    event = this.series.chart.pointer.normalize(event);
    this.onMouseOver(); // 激活点悬浮样式
    this.series.chart.tooltip.refresh(this); // 刷新提示框
    this.series.chart.xAxis[0].drawCrosshair(event, this); // 绘制十字准星
};

/**
 * X轴范围同步回调:缩放其中一张图表,同步其余图表X轴区间
 * 增加trigger标记避免循环调用
 */
function syncExtremes(e) {
    const thisChart = this.chart;
    if (e.trigger !== 'syncExtremes') {
        Highcharts.charts.forEach(chart => {
            if (chart !== thisChart) {
                if (chart.xAxis[0].setExtremes) {
                    chart.xAxis[0].setExtremes(
                        e.min,
                        e.max,
                        undefined,
                        false,
                        { trigger: 'syncExtremes' }
                    );
                }
            }
        });
    }
}

/**
 * 图表重置缩放联动:一张图表复原,所有图表同步复原
 */
function resetZoom(e) {
    if (e.resetSelection) {
        return;
    }
    Highcharts.charts.forEach(chart => {
        if (chart !== e.target) {
            chart.zoomOut();
        }
    });
}

(async () => {
    // 请求远程活动数据
    const activity = await fetch(
        'https://www.highcharts.com/samples/data/activity.json'
    ).then(res => res.json());

    // 循环数据集,动态创建DOM容器并生成每一张图表
    activity.datasets.forEach(function (dataset, i) {
        // 将x轴里程与y值组合成 [[x,y]] 格式
        dataset.data = dataset.data.map((val, j) => [activity.xData[j], val]);

        const chartDiv = document.createElement('div');
        chartDiv.className = 'chart';
        document.getElementById('container').appendChild(chartDiv);

        Highcharts.chart(chartDiv, {
            chart: {
                marginLeft: 40, // 统一左侧边距,图表左对齐
                spacingTop: 20,
                spacingBottom: 20,
                zooming: {
                    type: 'x' // 仅支持X轴横向框选缩放
                },
                events: {
                    selection: resetZoom
                }
            },
            title: {
                text: dataset.name,
                align: 'left',
                margin: 0,
                x: 30
            },
            credits: {
                enabled: false
            },
            legend: {
                enabled: false
            },
            xAxis: {
                crosshair: true,
                events: {
                    setExtremes: syncExtremes
                },
                labels: {
                    format: '{value} 公里'
                },
                accessibility: {
                    description: '里程',
                    rangeDescription: '0公里 至 6.5公里'
                }
            },
            yAxis: {
                title: {
                    text: null
                }
            },
            tooltip: {
                fixed: true,
                position: {
                    align: 'right',
                    relativeTo: 'spacingBox',
                    y: -2
                },
                padding: 0,
                pointFormat: '{point.y}',
                backgroundColor: 'none',
                headerFormat: '',
                shadow: false,
                style: {
                    fontSize: '18px'
                },
                valueDecimals: dataset.valueDecimals
            },
            series: [{
                data: dataset.data,
                name: dataset.name,
                type: dataset.type,
                color: Highcharts.getOptions().colors[i],
                fillOpacity: 0.3,
                tooltip: {
                    valueSuffix: ' ' + dataset.unit
                }
            }]
        });
    });
})();
</script>
</body>
</html>

核心模块逐段解析

1、全局鼠标事件监听

监听外层容器 mousemove、触摸事件,遍历全局 Highcharts.charts 实例数组,对每一张图表查找鼠标位置对应的数据点,调用自定义 highlight()

关键点:事件绑定在父容器,而不是单个图表,实现跨图表联动。

2、原型方法重写

  1. Highcharts.Pointer.prototype.reset:屏蔽原生重置逻辑。原生鼠标移出图表会立刻隐藏tooltip和十字线;重写之后悬浮信息不会消失,达到多图同步显示效果。
  2. Highcharts.Point.prototype.highlight:自定义点位高亮方法,封装三件事:激活点悬浮样式、刷新tooltip、绘制十字准星。

⚠️注意:修改内置原型属于侵入式写法,如果页面其他图表不需要联动,会受到该全局修改影响。

3、坐标轴缩放同步 syncExtremes

  • setExtremes:X轴范围发生变化时触发事件。
  • trigger: 'syncExtremes' 标记,防止循环回调死循环:A修改B坐标轴,B触发事件又回改A。

4、resetZoom 缩放重置联动

当执行图表还原缩放,遍历全部图表调用 zoomOut(),实现全部图表一起复原。

5、动态批量创建图表

  1. fetch 获取远程JSON,包含多组指标数据集与X轴里程数组;
  2. 循环数据集,JS动态创建div容器插入页面;
  3. 每个div实例化一张独立Highcharts图表;
  4. x轴单位改为中文"公里"。

业务场景与坑点总结

适用场景

  • 监控大屏多指标时序面板(流量、性能多维度纵向排布)
  • 运动轨迹多生理指标对比(本示例原始场景:距离‑心率‑海拔‑功率)
  • IoT设备多传感器同步时序对比。

开发注意事项

  1. 原型修改是全局生效:页面如果同时存在不需要联动的普通图表,该demo会干扰它们tooltip行为;解决方案:隔离页面,或者封装子类不要直接修改原型。
  2. 依赖 Highcharts.charts 全局实例数组;图表销毁时数组不会自动清理,长时间SPA页面要手动清理。
  3. 跨域问题:本地直接打开HTML访问 highcharts.com 的json会跨域;本地调试建议把 activity.json 下载为本地模拟数据。
  4. 只支持X轴同步,如果需要Y轴同步,需要扩展 syncExtremes 同时同步yAxis。

可扩展方向

  • 增加开关控制是否开启联动;
  • Y轴范围同步;
  • 替换成本地静态模拟数据消除fetch跨域;
  • 增加图表销毁清理逻辑适配SPA(Vue/React)。
相关推荐
躺柒3 小时前
读数据可视化25高维多元数据(上)
信息可视化·可视化·数据可视化·高维·多变·多元数据
2601_962077986 小时前
Python中数据可视化的新层次
python·plotly·数据可视化·交互式图表·单行代码
2601_962097369 小时前
GraphQL,Grafana和Dash
python·grafana·数据可视化·graphql·dash
金銀銅鐵1 天前
用 Mermaid 的 Treemap 对比各大洲的面积
数据可视化
一晌小贪欢1 天前
python-第29天:Python面向对象之多态与抽象类
开发语言·python·数据可视化·面向对象·python办公·python多态
躺柒1 天前
读数据可视化24跨媒体数据(下)
信息可视化·可视化·数据可视化·多媒体·超媒体·跨媒体
图扑软件2 天前
中篇・运笔|统一 DataModel 底座,HT UI 组件万物同源
javascript·低代码·ui·性能优化·数据可视化
DolitD2 天前
实时云渲染多应用并发:CELL容器技术突破3D隔离瓶颈
云原生·容器·实时互动·图形渲染·数据可视化
躺柒2 天前
读数据可视化23跨媒体数据(上)
信息可视化·数据分析·可视化·数据可视化·多媒体·超媒体·跨媒体