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

Chart.js多图表同步垂直标线异常:非活跃图表标线残留未清除

Fixing Persistent Vertical Lines on Inactive Chart.js Charts

我完全懂你遇到的糟心问题——鼠标在活跃图表上移动时,非活跃图表的垂直标线只会不断叠加,旧的线根本不会被清除,到最后整个图表都被线铺满了。这是因为你之前在单个图表的draw方法里循环给所有图表画标线,但非活跃图表不会主动触发完整重绘来清除旧内容,而且每个图表的draw被调用时都会重复画一遍,自然就堆成一团了。

下面是修正后的方案,核心思路是让每个图表独立管理自己的标线,同时通过全局鼠标事件同步所有图表的标线位置,确保每次重绘时都会先清除旧标线:

var charts = [];
$(document).ready(function () {
    // 全局变量存储当前鼠标在X轴的位置
    var currentMouseX = null;

    Chart.defaults.LineWithLine = Chart.defaults.line;
    Chart.controllers.LineWithLine = Chart.controllers.line.extend({
        draw: function(ease) {
            // 先调用父类的draw方法,这会自动清除画布并绘制图表主体
            Chart.controllers.line.prototype.draw.call(this, ease);
            
            var chart = this.chart;
            var ctx = chart.ctx;
            var yScale = chart.scales['y-axis-0'];

            // 如果有当前鼠标位置,才绘制标线
            if (currentMouseX !== null) {
                ctx.save();
                ctx.beginPath();
                ctx.moveTo(currentMouseX, yScale.top);
                ctx.lineTo(currentMouseX, yScale.bottom);
                ctx.lineWidth = 2;
                ctx.strokeStyle = '#07C';
                ctx.stroke();
                ctx.restore();
            }
        }
    });

    // 创建图表1
    var ctx1 = document.getElementById('myChart1').getContext('2d');
    var chart1 = new Chart(ctx1, {
        type: 'LineWithLine',
        data: {
            labels: ['Segment 1', 'Segment 2', 'Segment 3','Segment 4','Segment 5','Segment 6','Segment 7','Segment 8','Segment 9','Segment 10','Segment 11','Segment 12'],
            datasets: [{
                lineTension: 0,
                backgroundColor: "rgb(34,139,34)",
                borderColor: "rgb(34,139,34)",
                data: [14, 19, 20, 10, 6, 15, 8, 27, 25, 14, 36, 22],
                fill: false,
                pointRadius: 1.5,
                pointHoverRadius: 1,
                borderWidth :1.5
            }]
        },
        options: {
            maintainAspectRatio: false,
            hover: {
                mode: 'index',
                intersect: false,
            },
            legend: {
                display: false
            },
            tooltips: {
                mode: 'index',
                intersect: false,
            },
        }
    });

    // 创建图表2
    var ctx2 = document.getElementById('myChart2').getContext('2d');
    var chart2 = new Chart(ctx2, {
        type: 'LineWithLine',
        data: {
            labels: ['Segment 1', 'Segment 2', 'Segment 3','Segment 4','Segment 5','Segment 6','Segment 7','Segment 8','Segment 9','Segment 10','Segment 11','Segment 12'],
            datasets: [{
                lineTension: 0,
                backgroundColor: "rgb(34,139,34)",
                borderColor: "rgb(34,139,34)",
                data: [14, 11, 10, 20, 20, 15, 25, 15, 13, 14, 16, 8],
                fill: false,
                pointRadius: 1.5,
                pointHoverRadius: 1,
                borderWidth :1.5
            }]
        },
        options: {
            maintainAspectRatio: false,
            legend: {
                display: false
            },
            tooltips: {
                mode: 'index',
                intersect: false,
            },
        }
    });

    charts.push(chart1, chart2);

    // 全局鼠标移动事件,同步所有图表的标线位置
    $('canvas').on('mousemove', function(e) {
        var targetChart = charts.find(chart => chart.canvas === e.target);
        if (!targetChart) return;

        // 获取鼠标在图表坐标系中的X位置
        var chartArea = targetChart.chartArea;
        currentMouseX = e.offsetX - chartArea.left;

        // 确保X位置在图表范围内
        if (currentMouseX < 0 || currentMouseX > chartArea.width) {
            currentMouseX = null;
        }

        // 触发所有图表重绘,这样每个图表都会清除旧标线并绘制新的
        charts.forEach(chart => chart.update());
    });

    // 鼠标离开画布时清除标线
    $('canvas').on('mouseleave', function() {
        currentMouseX = null;
        charts.forEach(chart => chart.update());
    });
});

关键改动说明:

  • 移除跨图表循环绘制:每个图表只负责绘制自己的标线,避免重复叠加绘制的问题。
  • 全局鼠标事件同步位置:通过监听所有canvas的mousemove事件,计算当前鼠标在图表中的X坐标,然后触发所有图表重绘,保证标线位置同步。
  • 依赖父类draw自动清除:每次调用父类的draw方法时,Chart.js会自动清除画布并重新绘制图表主体,旧标线自然被清除,再绘制新的标线就不会有残留。
  • 鼠标离开时清空标线:确保鼠标移开后所有图表的标线都会及时消失。

这样修改后,不管是活跃还是非活跃图表,都会在鼠标移动时同步更新标线,既不会有残留问题,也不会出现卡顿的情况。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 12:52:39