用Highcharts开发动态主从图表|双图联动的时间序列图表示例

动态主从图表是什么图表?

------双图联动的时间序列图表(主图 + 明细图)

  • 上方大图 :明细图,显示选中区间的详细数据
  • 下方小图 :主图,显示全部数据 ,可拖拽选择区间

这是 主图 + 明细图联动(拖拽选择区间,上方图表自动放大显示)的经典 Highcharts 案例。

图表显示如何添加第二个图表,以接近Highcharts中的股票导航功能。单击并拖动下方图表以放大上方图表。

HTML全代码:保存为 HTML → 打开就能用

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>美元欧元汇率 - 主图+明细图联动</title>
    <style>
        #container {
            width: 100%;
            height: 700px;
            position: relative;
        }
        #detail-container {
            height: 80%;
        }
        #master-container {
            height: 20%;
            position: absolute;
            bottom: 0;
            left: 0;
            right: 0;
        }
    </style>
    <script src="https://code.highcharts.com/highcharts.js"></script>
</head>
<body>
    <div id="container"></div>

<script>
(async () => {
    // 1. 加载美元兑欧元汇率历史数据
    const data = await fetch(
        'https://www.highcharts.com/samples/data/usdeur.json'
    ).then(response => response.json());

    let detailChart; // 上方明细图表

    // ==============================================
    // 2. 创建【上方明细图表】:显示选中区间的详细曲线
    // ==============================================
    function createDetail(masterChart) {
        const detailData = [];
        const detailStart = data[0][0]; // 数据起始时间

        // 提取主图中需要显示到明细图的数据
        masterChart.series[0].data.forEach(point => {
            if (point.x >= detailStart) {
                detailData.push(point.y);
            }
        });

        // 初始化明细图表
        detailChart = Highcharts.chart('detail-container', {
            chart: {
                marginBottom: 120,
                reflow: false,
                marginLeft: 60,
                marginRight: 20,
                style: { position: 'absolute' }
            },
            credits: { enabled: false }, // 隐藏版权
            title: {
                text: '美元兑欧元历史汇率',
                align: 'left'
            },
            subtitle: {
                text: '在下方图表中拖动鼠标选择区间',
                align: 'left'
            },
            xAxis: { type: 'datetime' }, // 时间轴
            yAxis: {
                title: { text: null },
                maxZoom: 0.1
            },
            // 悬浮提示框
            tooltip: {
                format: '<b>{series.name}</b><br/>{x:%a, %b %e, %Y}:<br/>1 USD = {y:.2f} EUR',
                shared: true
            },
            legend: { enabled: false },
            // 数据点样式
            plotOptions: {
                series: {
                    marker: {
                        enabled: false,
                        states: { hover: { enabled: true, radius: 3 } }
                    }
                }
            },
            // 数据系列
            series: [{
                name: 'USD to EUR',
                pointStart: detailStart,
                pointInterval: 24 * 3600 * 1000, // 一天一个点
                data: detailData
            }],
            exporting: { enabled: false }
        });
    }

    // ==============================================
    // 3. 创建【下方主图表】:可拖拽选择时间区间
    // ==============================================
    function createMaster() {
        const maskFill = 'rgba(0,0,255,0.05)'; // 未选中区域半透明遮罩

        Highcharts.chart('master-container', {
            chart: {
                reflow: false,
                borderWidth: 0,
                backgroundColor: null,
                marginLeft: 60,
                marginRight: 20,
                zooming: { type: 'x' }, // 横向缩放

                // ==============================
                // 核心:拖拽选择区间触发事件
                // ==============================
                selection: function (event) {
                    const min = event.xAxis[0].min;
                    const max = event.xAxis[0].max;
                    const detailData = [];

                    // 提取选中区间内的数据
                    this.series[0].data.forEach(point => {
                        if (point.x > min && point.x < max) {
                            detailData.push([point.x, point.y]);
                        }
                    });

                    // 更新遮罩层(未选中区域变灰)
                    const xAxis = this.xAxis[0];
                    xAxis.removePlotBand('mask-before');
                    xAxis.addPlotBand({
                        id: 'mask-before',
                        from: data[0][0],
                        to: min,
                        color: maskFill
                    });
                    xAxis.removePlotBand('mask-after');
                    xAxis.addPlotBand({
                        id: 'mask-after',
                        from: max,
                        to: data[data.length - 1][0],
                        color: maskFill
                    });

                    // 将选中数据更新到上方明细图
                    detailChart.series[0].setData(detailData);
                    return false;
                }
            },
            title: { text: null },
            xAxis: {
                type: 'datetime',
                maxZoom: 14 * 24 * 3600000, // 最小14天
                plotBands: [{
                    id: 'mask-before',
                    from: data[0][0],
                    to: data[data.length - 1][0],
                    color: maskFill
                }]
            },
            yAxis: {
                gridLineWidth: 0,
                labels: { enabled: false },
                min: 0.6
            },
            plotOptions: {
                series: {
                    fillColor: {
                        linearGradient: [0, 0, 0, 70],
                        stops: [
                            [0, Highcharts.getOptions().colors[0]],
                            [1, 'rgba(255,255,255,0)']
                        ]
                    },
                    lineWidth: 1,
                    marker: { enabled: false },
                    enableMouseTracking: false
                }
            },
            series: [{
                type: 'area',
                name: 'USD to EUR',
                pointInterval: 24 * 3600 * 1000,
                pointStart: data[0][0],
                data: data
            }],
            exporting: { enabled: false }
        }, createDetail); // 创建完成后 → 初始化明细图
    }

    // ==============================================
    // 4. 创建两个图表容器(上方明细 + 下方主图)
    // ==============================================
    const container = document.getElementById('container');
    container.style.position = 'relative';
    container.innerHTML = `
        <div id="detail-container"></div>
        <div id="master-container"></div>
    `;

    // 启动:先创建主图
    createMaster();
})();
</script>
</body>
</html>

核心功能

✅ 你在下方图表拖动鼠标

✅ 上方图表自动放大显示该段数据

✅ 未选中区域自动变灰(遮罩层)

✅ 实时切换、无刷新、流畅交互

这是金融、股票、气象、监控仪表盘最常用的交互模式。

技术亮点

  • 异步加载远程 JSON
  • 双图表联动通信
  • 拖拽选区(selection 事件)
  • 动态更新数据
  • 遮罩层高亮选中区间
  • 时间轴格式化
  • 悬浮提示美观

适用场景

  • 股票 K 线、走势图
  • 汇率、价格监控
  • 服务器监控、温度监控
  • 任何需要区间放大查看的图表
相关推荐
阿俊-全栈开发2 小时前
LikeShop 版本升级与备份:从旧版平滑迁移的完整步骤
jvm·数据库·oracle·开源·likeshop·likeshop开源商城
志尊宝2 小时前
Vue3 零基础每日笔记(076):防抖节流的正确使用——搜索框、按钮防重复提交、滚动监听
javascript·vue.js·笔记
姜鱼问生3 小时前
docker exec 排查容器问题:从日志到进程
运维·网络·数据库·docker·容器
能源革命3 小时前
Vue 网格拖拽布局组件全景指南:7 大方案深度对比
前端·javascript·vue.js
雷工笔记3 小时前
KingFusion 驾驶舱开发:ECharts小时模板实现动态时间窗口
javascript
知野小兔3 小时前
JavaScript中Map和Set详解
javascript
狗凯之家源码网3 小时前
AI 步数修改提交系统源码应用场景与落地指南
android·数据库·人工智能·运动步数
穆梓兰煊3 小时前
AI供应链安全不再只是包安全,还涉及模型与数据
前端·数据库
北风toto3 小时前
数据库笔记:Armstrong 公理系统与集合论的深度辨析
数据库·笔记