如何选择日期范围(如 onClick 但拖动/选择)拖动、如何选择、范围、日期

2023-09-07 12:02:46 作者:以爱之名

我想使用 Chart.js 重写 vizwit,但我很难弄清楚如何让日期/时间表交互工作.如果您尝试在此演示上选择日期范围,您会发现它会过滤其他图表.如何让 Chart.js 让我在其时间刻度图表上选择这样的范围?似乎默认情况下它只允许我点击一个特定的日期点.

I'd like to rewrite vizwit using Chart.js, and I'm having a hard time figuring out how to get the date/time chart interaction to work. If you try selecting a date range on this demo, you'll see that it filters the other charts. How do I get Chart.js to let me select a range like that on its time scale chart? It seems like by default it only lets me click on a specific date point.

感谢您的宝贵时间.

推荐答案

基于@jordanwillis 和您的答案,您可以通过在图表顶部放置另一个画布轻松实现您想要的任何事情.只需将 pointer-events:none 添加到它的样式中,以确保它不会干扰图表的事件.无需使用注解插件.例如(在此示例中,canvas 是原始图表画布,overlay 是放置在顶部的新画布):

Building on @jordanwillis's and your answers, you can easily achieve anything you want, by placing another canvas on top on your chart. Just add pointer-events:none to it's style to make sure it doesn't intefere with the chart's events. No need to use the annotations plugin. For example (in this example canvas is the original chart canvas and overlay is your new canvas placed on top):

var options = {
  type: 'line',
  data: {
    labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
    datasets: [{
        label: '# of Votes',
        data: [12, 19, 3, 5, 2, 3],
        borderWidth: 1
      },
      {
        label: '# of Points',
        data: [7, 11, 5, 8, 3, 7],
        borderWidth: 1
      }
    ]
  },
  options: {
    scales: {
      yAxes: [{
        ticks: {
          reverse: false
        }
      }]
    }
  }
}

var canvas = document.getElementById('chartJSContainer');
var ctx = canvas.getContext('2d');
var chart = new Chart(ctx, options);
var overlay = document.getElementById('overlay');
var startIndex = 0;
overlay.width = canvas.width;
overlay.height = canvas.height;
var selectionContext = overlay.getContext('2d');
var selectionRect = {
  w: 0,
  startX: 0,
  startY: 0
};
var drag = false;
canvas.addEventListener('pointerdown', evt => {
  const points = chart.getElementsAtEventForMode(evt, 'index', {
    intersect: false
  });
  startIndex = points[0]._index;
  const rect = canvas.getBoundingClientRect();
  selectionRect.startX = evt.clientX - rect.left;
  selectionRect.startY = chart.chartArea.top;
  drag = true;
  // save points[0]._index for filtering
});
canvas.addEventListener('pointermove', evt => {

  const rect = canvas.getBoundingClientRect();
  if (drag) {
    const rect = canvas.getBoundingClientRect();
    selectionRect.w = (evt.clientX - rect.left) - selectionRect.startX;
    selectionContext.globalAlpha = 0.5;
    selectionContext.clearRect(0, 0, canvas.width, canvas.height);
    selectionContext.fillRect(selectionRect.startX,
      selectionRect.startY,
      selectionRect.w,
      chart.chartArea.bottom - chart.chartArea.top);
  } else {
    selectionContext.clearRect(0, 0, canvas.width, canvas.height);
    var x = evt.clientX - rect.left;
    if (x > chart.chartArea.left) {
      selectionContext.fillRect(x,
        chart.chartArea.top,
        1,
        chart.chartArea.bottom - chart.chartArea.top);
    }
  }
});
canvas.addEventListener('pointerup', evt => {

  const points = chart.getElementsAtEventForMode(evt, 'index', {
    intersect: false
  });
  drag = false;
  console.log('implement filter between ' + options.data.labels[startIndex] + ' and ' + options.data.labels[points[0]._index]);  
});

<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.0/Chart.js"></script>

<body>
  <canvas id="overlay" width="600" height="400" style="position:absolute;pointer-events:none;"></canvas>
  <canvas id="chartJSContainer" width="600" height="400"></canvas>
</body>

请注意,我们将事件和坐标基于原始画布,但我们在叠加层上进行绘制.这样我们就不会弄乱图表的功能.

Notice we're basing our events and coordinates on the original canvas, but we draw on the overlay. This way we don't mess the chart's functionality.