React, D3 and Canvas

I’m having some trouble getting d3.drag to work while using canvas in a react application. My data nodes are simply objects with coordinates, f.e. {x: 3, y:5} existing in my redux state.

The nodes does not seem to be moving during drag event though I have found the correct subject. I have used this example as an inspiration

It would be really nice with an example on the D3 page with a zoomable and draggable stateful application using React.

My drag functionality

 // Drag

  const dragSubject = (e: DragEvent) => {
    const x = zoomedXScale.invert(e.x);
    const y = zoomedYScale.invert(e.y);
    let dx, dy;
    const temp = data.find((node) => {
      const tempNode = node;
      dx = x - tempNode.x;
      dy = y - tempNode.y;
      if (dx * dx + dy * dy < 5 * 5) {
        tempNode.x = x;
        tempNode.y = y;
        return tempNode;
      }
    });
    return temp;
  };

  const dragStarted = (e) => {
    const { x }: { x: number } = e;
    const { y }: { y: number } = e;
    e.subject.fx = zoomedXScale.invert(x);
    e.subject.fy = zoomedYScale.invert(y);
  };

  const dragged = (e) => {
    console.log('dragged');
    const { x }: { x: number } = e;
    const { y }: { y: number } = e;
    e.subject.fx = zoomedXScale.invert(x);
    e.subject.fy = zoomedYScale.invert(y);
  };

  const dragEnded = (e) => {
    console.log(e.subject);
    console.log('drag is ended');
  };

And my code using the Canvas and d3 svg looks like this

const drag = d3
    .drag()
    .subject(dragSubject)
    .on('start', dragStarted)
    .on('drag', dragged)
    .on('end', dragEnded);

  // UseEffects
  useEffect(() => {
    if (canvasRef.current) {
      const canvasObj: HTMLCanvasElement = canvasRef.current;
      setContext(canvasObj.getContext('2d'));
    }
  });

  // UseEffect for zoom - Calls zoomed on canvasRef on d3.ZoomEvent
  useEffect(() => {
    d3.select(canvasRef.current).call(drag).call(zoomed).call(draw);
  }, [zoomed, drag]);

My drawing function for the canvas to draw my points

const draw = () => {
    if (context) {
      context.save();
      context.clearRect(0, 0, chartWidth, chartHeight);
      context.beginPath();
      data.forEach((point) => {
        drawPoint(point);
      });
      context.fill();
      context.restore();
    }
  };

Have you an observable notebook with this in it?

I have the start of one, I’ve never created a notebook here before, and certainly not using React to do it, so it was a bit tricky.

The idea in the end is to have a lineChart with axises and in it some data points connected with lines in the chart. Connected to these fixed data points will be a box containing more detailed information which will be draggable and connected with a line to this simple data point.

What I am trying to do now is just to get some simple data points to render, behave correctly when zoomed and also have the ability to be dragged around.

Link to react/d3/drag/zoom notebook

The link to the notebook is now updated, it does not run here yet, but in a create-react-application one should be able to draw a canvas and drag a set of circles around in a zoomable line chart.

The problem was originated in the dragSubject function where I did not translate the subjects x- and y coordinates correctly.

Use link above to see React code.