Search code examples
javascriptcanvaschart.jshtml5-canvascubic-bezier

Calculating point coordinates on a line in chartjs?


In this draggable ChartJS demo is it possible to derive a set of interpolated coordinates along one of the curves that ChartJS plots?

    var options = {
      type: 'line',
      data: {
        labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
        datasets: [{
            label: '# of Votes',
            data: [12, 19, 3, 5, 2, 3],
            fill: true,
            tension: 0.4,
            borderWidth: 1,
            pointHitRadius: 25
          },
          {
            label: '# of Points',
            data: [7, 11, 5, 8, 3, 7],
            fill: true,
            tension: 0.4,
            borderWidth: 1,
            pointHitRadius: 25
          }
        ]
      },
      options: {
        scales: {
          y: {
            min: 0,
            max: 20
          }
        },
        onHover: function(e) {
          const point = e.chart.getElementsAtEventForMode(e, 'nearest', { intersect: true }, false)
          if (point.length) e.native.target.style.cursor = 'grab'
          else e.native.target.style.cursor = 'default'
        },
        plugins: {
          dragData: {
            round: 1,
            showTooltip: true,
            onDragStart: function(e, datasetIndex, index, value) {
              // console.log(e)
            },
            onDrag: function(e, datasetIndex, index, value) {              
              e.target.style.cursor = 'grabbing'
              // console.log(e, datasetIndex, index, value)
            },
            onDragEnd: function(e, datasetIndex, index, value) {
              e.target.style.cursor = 'default' 
              // console.log(datasetIndex, index, value)
            },
          }
        }
      }
    }

    var ctx = document.getElementById('chartJSContainer').getContext('2d');
    window.test = new Chart(ctx, options);
<head>
  <meta charset="utf-8">
  <title>Chart.js Drag Data Points Plugin</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.6.0/chart.min.js"></script>
  <script src="assets/chartjs-plugin-dragdata.min.js"></script>
  <style>
    html,
    body {
      margin: 0;
      padding: 0;
    }

    canvas {
      background-color: #eee;
      position: absolute;
    }
  </style>
</head>



<canvas id="chartJSContainer" style="height: 90%; width: 90%;"></canvas>

The demo plots an array of 5 values.

data: [12, 19, 3, 5, 2, 3],

Is there a way to use Javascript produce an array of 10 y values where the x coordinates are evenly spaced along the curve?

So for example if the canvas width is 200px, then the x coordinates would increment in values of 20px, and the y value would be the corresponding point on the bezier curve.

I looked through the ChartJS API documentation, and I don't see an API for the bezier curve function for each plot exposed, but perhaps there is a way around this?


Solution

  • Depending for what you want to use this, an easy workaround/solution is:

    • fill in the labels property the correct amount of units need, for example ["Red", "Blue", "Yellow", "Green", "Purple", "Orange", "Blue", "Yellow", "Green", "Purple", "Green"]
    • fill the array with some empty values*(neatly spaced, you could do this also with code)*, to match the size of the labels-array, like so [7, null, 11, null, 5 , null, 8, null, 3, null, 7]
    • and use the spanGaps: true property so that chartjs will connect the points.

    Like this chartjs, fills in the gaps and you can space out the chart without manually calculating any extra values.

    Small Demo:

    var options = {
        type: 'line',
        data: {
          labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange", "Blue", "Yellow", "Green", "Purple", "Green"],
          datasets: [
            {
              label: '# of Points',
              data: [7, null, 11, null,  5 , null,  8, null,   3, null,  7],
              fill: true,
              tension: 0.4,
              borderWidth: 1,
              pointHitRadius: 25,
              spanGaps: true
            }
          ]
        },
        options: {
          scales: {
            y: {
              min: 0,
              max: 20
            }
          },
        }
      }
    
      let ctx = document.getElementById('chartJSContainer').getContext('2d');
      new Chart(ctx, options);
        html, body { margin: 0; padding: 0;}
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.6.0/chart.min.js"></script>
    <canvas id="chartJSContainer" style="height: 180px; width: 500px;"></canvas>