react-force-graph icon indicating copy to clipboard operation
react-force-graph copied to clipboard

Prevent dragging node off canvas

Open oneillsp96 opened this issue 3 years ago • 2 comments

I imagine this functionality exists, I am just having trouble figuring out where/how. I would simply like to prevent the user from dragging a node off the canvas, like in this example:

https://bl.ocks.org/mbostock/1129492

oneillsp96 avatar Feb 03 '22 03:02 oneillsp96

the following code is basically working for me, although (1) I don't know how to get the node radius in pixels (or graph units) and (2) when I drag a node up against the edge, it causes all the nodes to stutter (quickly shake a little)

function handleEngineTick() {
        // @ts-ignore
        const container = forceRef.current;
        graphDataMemoized.nodes.forEach((node: any) => {
            const coordinates = container.graph2ScreenCoords(node.x, node.y);
            const boxWidth = displayWidth;
            const boxHeight = displayHeight;
            const nodeRadius = NODE_R * node.val;
            const margin = 15; // how to get node radius? If we can get it we don't need margin

            // right boundary
            if (coordinates.x > (boxWidth - nodeRadius - margin)) {
                const desiredScreenX = boxWidth - nodeRadius - margin;
                const graphUnits = container.screen2GraphCoords(desiredScreenX, node.y);
                node.x = graphUnits.x;
            }

            // left boundary
            if (coordinates.x < 0 + nodeRadius + margin) {
                const desiredScreenX = nodeRadius + margin;
                const graphUnits = container.screen2GraphCoords(desiredScreenX, node.y);
                node.x = graphUnits.x;
            }

            // bottom boundary
            if (coordinates.y > (boxHeight - nodeRadius - margin)) {
                const desiredScreenY = boxHeight - nodeRadius - margin;
                const graphUnits = container.screen2GraphCoords(node.x, desiredScreenY);
                node.y = graphUnits.y;
            }

            // top boundary
            if (coordinates.y < 0 + nodeRadius + margin) {
                const desiredScreenY = nodeRadius + margin;
                const graphUnits = container.screen2GraphCoords(node.x, desiredScreenY);
                node.y = graphUnits.y;
            }
        });

    }

oneillsp96 avatar Feb 03 '22 14:02 oneillsp96

Hey there! Thanks for your solution. I've used it in my project.

Concerning the way to get the radius, I found it inspecting the source code of force-graph module. See this link: https://github.com/vasturiano/force-graph/blob/06e372da6a02b838395e7b85c26fd05d5e63d31c/src/force-graph.js#LL294C7-L294C90

export function getNodeRadius(node) {
  return Math.sqrt(Math.max(0, node.val || 1)) * NODE_REL_SIZE
}

Hope it helps! And thank you a lot :)

simonfan avatar May 31 '23 08:05 simonfan