如果鼠标移动过快,Chart.js `mousemove`事件无法正确注册

k97glaaz  于 11个月前  发布在  Chart.js
关注(0)|答案(1)|浏览(171)

我正在使用Chart.js 4.4.0创建一个具有十字准线效果的图表,类似于Flot.js crosshair插件。在this YouTube guide之后,我生成了以下示例代码:

<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. */
      /* https://www.youtube.com/watch?v=za2cQFObvWQ */
      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>

字符串
当用户将鼠标移到图表上时,这将成功创建十字准线。
问题是,如果我将鼠标指针快速移出图表,线条并不总是以应有的方式消失。它们只是停留在我将鼠标移出的边缘附近。这种情况并不总是发生,当我慢慢穿过边缘时几乎不会发生。这几乎就像代码无法“注册”光标是否移动过快而超出其边界。
This question描述了一个类似的问题,但提供的唯一解决方案是将侦听器附加到图表的包含节点,而不是图表本身或HTML <body>
This answer也解决了这个问题,但只提供了如何解决这个问题的一般描述。我打算开始尝试这个建议,但我预计这将需要一两周的努力,所以我在这里发帖,希望有人会有更好的解决方案或工作代码在此期间发布。(这个答案已经超过13年了,所以现在有一个更好的解决方案是合理的希望)。
我如何调整上面的代码,以保证无论指针移动的速度如何,只要指针在图表区域之外,十字准线就会消失?

nwwlzxa7

nwwlzxa71#

chart.update('none');的调用绘制了没有十字准线的图表,函数crosshairs中的所有后续操作都是绘制实际的十字准线线。
这意味着要删除十字准线,您的方法依赖于在图表容器内部但在chartArea外部触发的mousemove事件,因此if条件(x >= left && x <= right && y >= top && y <= bottom)至少为false一次(这是唯一的情况下,当chart.update('none')是 * 不 * 后跟着线条绘制)这是mousemove应该触发的一个非常小的边界,这就是为什么它很容易被(不是很)快速的鼠标移动跳过。
删除十字准线的正确方法是为mouseleave注册一个处理程序,该处理程序只绘制图表而不绘制其他内容:

container.addEventListener('mouseleave', (e) => {
    cjs_plot.update('none')
});

字符串
下面是您的代码片段,

/* 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. */
/* https://www.youtube.com/watch?v=za2cQFObvWQ */
container.addEventListener('mousemove', (e) => {
    crosshair(cjs_plot, e);
})

container.addEventListener('mouseleave', (e) => {
    cjs_plot.update('none')
})

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');
    
    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
<div id="chart_container" class="plot" style="width:50%;">
   <div>
     <canvas id="myChart"></canvas>
   </div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.0/chart.umd.js" integrity="sha512-6HrPqAvK+lZElIZ4mZ64fyxIBTsaX5zAFZg2V/2WT+iKPrFzTzvx6QAsLW2OaLwobhMYBog/+bvmIEEGXi0p1w==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

的数据

相关问题