Chart.js鼠标快速移出时十字线未消失问题及解决需求
解决Chart.js十字线快速移出不消失的问题
我使用Chart.js 4.4.0实现类似Flot.js的十字线效果,参考相关指南编写了代码,能实现鼠标悬停时显示十字线,但存在问题:鼠标快速移出图表区域时,十字线常卡在边缘无法消失,缓慢移出则正常,即使将事件监听器绑定到图表容器也无法解决。需要确保光标在图表区域外时十字线必定消失的适配方案。
原代码
<html> <head> <script type="text/javascript" src="../include/chart-4.4.0.umd.js"></script> </head> <body> <div id="chart_container" class="plot" style="width:50%;"> <div> <canvas id="myChart"></canvas> </div> </div> <script> /* The Chart.js context */ const ctx = document.getElementById('myChart'); /* The Chart container node, used for crosshair */ let container = document.getElementById('chart_container'); let chart_data = { datasets: [{ data: [{x:0, y:1}, {x:1, y:1}, {x:1, y:2}, {x:2, y:2}, {x:2, y:3}, {x:3, y:3}, {x:3, y:1}, {x:4, y:1}], borderWidth: 3, /* For the crosshair to work, we must disable other * tooltip hover effects: */ pointHoverRadius: 0, pointHitRadius: 0, pointRadius: 0 }] }; /* Chart.js chart options object for the Chart.js * constructor, defined below. */ let chart_options = { showLine: true, pointStyle: false, borderColor: 'rgba(255,204,0,1)', borderWidth: 1, fill: '+1', scales: { y: {beginAtZero: true} }, }; /* Create the Chart.js chart */ let cjs_plot = new Chart(ctx, { type: 'scatter', data: chart_data, options: chart_options, }); /* Implementing the crosshair effect previously done * using the Flot.js 'crosshair' plugin. */ container.addEventListener('mousemove', (e) => { crosshair(cjs_plot, e); }) function crosshair(chart, mousemove) { /* Setting `update` to 'none' means the refresh is * invisible so that we don't see previous crosshair * lines as the mousemoves. */ chart.update('none'); //console.log(mousemove); const x = mousemove.offsetX; const y = mousemove.offsetY; const { ctx, chartArea: {top, bottom, left, right, width, height} } = chart; ctx.save(); /* Draw items */ /* rgba(255,0,0,1) is solid red */ ctx.strokeStyle = 'rgba(255,0,0,1)'; ctx.lineWidth = 1; /* Only draw crosshairs if mouse is inside the chart area. * Remember 'y' is measured top-down. */ if(x >= left && x <= right && y >= top && y <= bottom) { /* Draw horizontal line */ ctx.beginPath(); ctx.moveTo(left, y); ctx.lineTo(right, y); ctx.stroke(); ctx.closePath(); /* Draw vertical line */ ctx.beginPath(); ctx.moveTo(x, top); ctx.lineTo(x, bottom); ctx.stroke(); ctx.closePath(); } ctx.restore(); } // End of crosshair() function </script> </body> </html>
解决方案
问题根源是鼠标快速移出时,可能直接跳过容器边缘,导致最后一次mousemove事件的坐标仍被判定在图表区域内,或者完全没触发容器的mousemove事件。通过以下两点修改解决:
- 新增
mouseleave事件监听,当鼠标离开容器时强制清除十字线 - 优化十字线绘制逻辑,确保每次绘制前都基于图表原始状态重绘
修改后的完整代码
<html> <head> <script type="text/javascript" src="../include/chart-4.4.0.umd.js"></script> </head> <body> <div id="chart_container" class="plot" style="width:50%;"> <div> <canvas id="myChart"></canvas> </div> </div> <script> const ctx = document.getElementById('myChart'); let container = document.getElementById('chart_container'); let chart_data = { datasets: [{ data: [{x:0, y:1}, {x:1, y:1}, {x:1, y:2}, {x:2, y:2}, {x:2, y:3}, {x:3, y:3}, {x:3, y:1}, {x:4, y:1}], borderWidth: 3, pointHoverRadius: 0, pointHitRadius: 0, pointRadius: 0 }] }; let chart_options = { showLine: true, pointStyle: false, borderColor: 'rgba(255,204,0,1)', borderWidth: 1, fill: '+1', scales: { y: {beginAtZero: true} }, }; let cjs_plot = new Chart(ctx, { type: 'scatter', data: chart_data, options: chart_options, }); // 同时监听mousemove和mouseleave事件 container.addEventListener('mousemove', (e) => { crosshair(cjs_plot, e); }); container.addEventListener('mouseleave', () => { // 鼠标离开时,强制更新图表清除十字线 cjs_plot.update('none'); }); function crosshair(chart, mousemove) { // 每次绘制前先更新图表,清除之前的十字线 chart.update('none'); const x = mousemove.offsetX; const y = mousemove.offsetY; const { ctx, chartArea: {top, bottom, left, right} } = chart; ctx.save(); ctx.strokeStyle = 'rgba(255,0,0,1)'; ctx.lineWidth = 1; if(x >= left && x <= right && y >= top && y <= bottom) { // 绘制水平十字线 ctx.beginPath(); ctx.moveTo(left, y); ctx.lineTo(right, y); ctx.stroke(); ctx.closePath(); // 绘制垂直十字线 ctx.beginPath(); ctx.moveTo(x, top); ctx.lineTo(x, bottom); ctx.stroke(); ctx.closePath(); } ctx.restore(); } </script> </body> </html>
关键修改说明
- 新增
mouseleave事件监听器:当鼠标离开容器区域时,直接调用chart.update('none')强制重绘图表,清除残留的十字线 - 保持
mousemove事件的逻辑不变,但确保每次绘制十字线前都先重置图表状态,避免旧的十字线残留
这样无论鼠标是快速还是缓慢移出,只要离开容器范围,十字线都会被立即清除。
内容的提问来源于stack exchange,提问作者Borea Deitz
相关产品推荐
相关产品推荐

