When I dragged the datapoint out of chart's ticks limit like from max x and y axis value, the canvas increase the limits too fast. How can I control this scaling speed? So that it increase with specific number defined in the config of chart.

Here is the js fiddle link. https://jsfiddle.net/rz7pw6j0/67/

JS

(function () {
  let chartInstance;
  let chartElement;

  function createChart(chartElement) {
    const chartConfig = {
        type: 'scatter',
        data: {
          datasets: [
            {
              data: [
                {
                  x: 0,
                  y:0
                }
              ],
              fill: true,
              showLine: true,
              lineTension: 0
            }
          ]
        },
        options: {
          legend: {
            display: false,
          },
          layout: {
            padding: {
              left: 50,
              right: 50,
              top: 0,
              bottom: 0
            }
          },
          title: {
            display: false,
            text: 'Chart.js Interactive Points',
          },
          scales: {
            xAxes: [
              {
                type: 'linear',
                display: true,
                padding: 20,
                paddingLeft: 10,
                paddingRight: 10,
                paddingTop: 10,
                paddingBottom: 10,
                scaleLabel: {
                  display: true,
                  labelString: 'Time',
                },
                ticks: {
                  suggestedMin: -5,
                  suggestedMax: 5,
                  stepValue: 1,
                }
              }
            ],
            yAxes: [
              {
                type: 'linear',
                display: true,
                scaleLabel: {
                  display: true,
                  labelString: 'Weight'
                },
                paddingLeft: 10,
                paddingRight: 10,
                paddingTop: 10,
                paddingBottom: 10,
                ticks: {
                  suggestedMin: 0,
                  suggestedMax: 3,
                  stepValue: 1
                },
              }
            ]
          },
          responsive: true,
          maintainAspectRatio: true,
          tooltips: {
            intersect: true,
          }
        }
      };
    chartInstance = new Chart(chartElement.getContext('2d'), chartConfig);
    let element = null;
    let scaleY = null;
    let scaleX = null;
    let datasetIndex = null;
    let index = null;
    let valueY = null;
    let valueX = null;
    function onGetElement() {
      const event = d3.event.sourceEvent;
      element = chartInstance.getElementAtEvent(event)[0];
      if (!element) {
        chartClickHandler(event);
        return;
      }
      if (event.shiftKey) {
        const tempDatasetIndex = element['_datasetIndex'];
        const tempIndex = element['_index'];
        chartInstance.data.datasets[tempDatasetIndex].data = chartInstance
          .data.datasets[tempDatasetIndex].data.filter((v, i) => i !== tempIndex);
        chartInstance.update();
        return;
      }
      scaleY = element['_yScale'].id;
      scaleX = element['_xScale'].id;
    }
    function onDragStart() {
      const event = d3.event.sourceEvent;
      datasetIndex = element['_datasetIndex'];
      index = element['_index'];
      valueY = chartInstance.scales[scaleY].getValueForPixel(event.offsetY);
      valueX = chartInstance.scales[scaleX].getValueForPixel(event.offsetX);
      chartInstance.data.datasets[datasetIndex].data[index] = {
        x: valueX,
        y: valueY
      };
        chartInstance.update(0);
    }
    function onDragEnd() {
      if (
      chartInstance.data.datasets[datasetIndex] &&
      chartInstance.data.datasets[datasetIndex].data) {
        chartInstance.data.datasets[datasetIndex].data.sort((a, b) => a.x - b.x > 0 ? 1 : -1);
        chartInstance.update(0);
      }
      element = null;
      scaleY = null;
      scaleX = null;
      datasetIndex = null;
      index = null;
      valueY = null;
      valueX = null;
    }

    d3.select(chartInstance.chart.canvas).call(
      d3.drag().container(chartInstance.chart.canvas)
        .on('start', onGetElement)
        .on('drag', onDragStart)
        .on('end', onDragEnd)
    );
  }


    function chartClickHandler (event) {
    let scaleRef;
    let valueX = 0;
    let valueY = 0;
    Object.keys(chartInstance.scales).forEach((scaleKey) => {
      scaleRef = chartInstance.scales[scaleKey];
      if (scaleRef.isHorizontal() && scaleKey === 'x-axis-1') {
        valueX = scaleRef.getValueForPixel(event.offsetX);
      } else if (scaleKey === 'y-axis-1') {
        valueY = scaleRef.getValueForPixel(event.offsetY);
      }
    });
    const newPoint = {
      x: valueX,
      y: valueY
    };
    const dataSeries = chartInstance.data.datasets[0].data;
    for (let i = 0; i < dataSeries.length; i++) {
      if (dataSeries[i].x === newPoint.x) {
        dataSeries.y = newPoint.y;
        chartInstance.update();
        return;
      }
    }
    let inserted = false;
    for (let j = dataSeries.length - 1; j >= 0; j--) {
      if (dataSeries[j].x > newPoint.x) {
        dataSeries[j + 1] = dataSeries[j];
      } else {
        dataSeries[j + 1] = newPoint;
        inserted = true;
        break;
      }
    }
    if (!inserted) {
      dataSeries.push(newPoint);
    }
    chartInstance.update();
    }

  chartElement = document.getElementById("chart");
  createChart(chartElement);
})();

HTML

<body>

 <div>Chart Drag and Click Test</div>
<div class="wrapper">
        <canvas id="chart"></canvas>
</div>
</body>

CSS

.wrapper {
  display: "block";
}

I want to control the scaling speed.

1 Answers

0
nagix On Best Solutions

If a dragStart event occurs beyond the scale limits, the increment should be a fixed value to avoid the issue you mentioned. Also, ticks.min and ticks.max should be set for the same purpose. Below is a sample jsfiddle and code (you can control speed by step).

https://jsfiddle.net/oLrk3fb2/

function onDragStart() {
  const event = d3.event.sourceEvent;
  const scales = chartInstance.scales;
  const scaleInstanceY = scales[scaleY];
  const scaleInstanceX = scales[scaleX];
  const scalesOpts = chartInstance.options.scales;
  const ticksOptsX = scalesOpts.xAxes[0].ticks;
  const ticksOptsY = scalesOpts.yAxes[0].ticks;
  const step = 1;

  datasetIndex = element['_datasetIndex'];
  index = element['_index'];
  valueY = scaleInstanceY.getValueForPixel(event.offsetY);
  valueX = scaleInstanceX.getValueForPixel(event.offsetX);

  if (valueY < scaleInstanceY.min) {
    ticksOptsY.min = valueY = scaleInstanceY.min - step;
  }
  if (valueY > scaleInstanceY.max) {
    ticksOptsY.max = valueY = scaleInstanceY.max + step;
  }
  if (valueX < scaleInstanceX.min) {
    ticksOptsX.min = valueX = scaleInstanceX.min - step;
  }
  if (valueX > scaleInstanceX.max) {
    ticksOptsX.max = valueX = scaleInstanceX.max + step;
  }

  chartInstance.data.datasets[datasetIndex].data[index] = {
    x: valueX,
    y: valueY
  }
  chartInstance.update(0);
}