question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

dynamic floating edges

See original GitHub issue

Hi, Is there a way to create floating edges like on react-digraph that update whenever you move an element? I tried using the getOutgoers and getIncomers functions for each node, then iterating on each one to create a dynamic handle.

I start by accessing the elements via the store:

const elements = useStoreState((store) => store.elements);

and then create a custom node:

 const incomers = getIncomers(props, elements);
    const outgoers = getOutgoers(props, elements);
    const incomersLen = incomers.length;
    const joinedArr = [...incomers, ...outgoers];

return joinedArr.map((element, index) => {
      const handleType = index < incomersLen ? "target" : "source";
      const calculatedHandlePosition = calculateHandlePosition(
        props,
        element,
        handleType
      );
      const { handlePosition, positionDeviation } = calculatedHandlePosition;
      const positionStyleObj = {
        top: "left",
        bottom: "left",
        left: "bottom",
        right: "bottom",
      };
      const handleStyle = {
        [positionStyleObj[handlePosition]]: `${positionDeviation}%`,
      };

      const handleId = `${handleType}-${id}-${element.id}`;
      return (
        <Handle
          type={handleType}
          position={handlePosition}
          style={handleStyle}
          id={handleId}
          key={handleId}
        />
      );
    });

calculatedHandlePosition just extracts the x,y coordinates of each element and then calculates the angle between them and based on that return the handle position. After doing that I noticed that the handle moves but the edge doesn’t move with it, plus the function doesn’t take into consideration the element dimension. Anyway I really wish there could be an easier (possibly out of the box) way to accomplish this, since it’s not the most convenient and I’m sure efficient way

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:13 (3 by maintainers)

github_iconTop GitHub Comments

2reactions
lomri123commented, Jan 26, 2021

@obaid You need to wrap your flow chart component with the provider (find it in their docs). This would give you real time access to node coordinates. What I used is a custom edge and node components. You need to calculate the handles and edges dynamically. For example my custom node component has a function in it called returnHandles which takes the current node (via id). gets its incomers and outgoers and then uses calculateHandlePosition(firstNode, secondNode, handleType) for each pair. The passed nodes have all their details - x,y coordinates, width and length (which is all you need to calculate the handle position):


  const elements = useStoreState((store) => store.elements);
  const myNodes = elements.filter((el) => el.id === id);

const returnHandles = () => {
    const incomers = getIncomers(props, elements);
    const outgoers = getOutgoers(props, elements);
    const incomersLen = incomers.length;
    const joinedArr = [...incomers, ...outgoers];

    return joinedArr.map((element, index) => {
      const handleType = index < incomersLen ? "target" : "source";
      const calculatedHandlePosition = calculateHandlePosition(
        myNodes[0],
        element,
        handleType
      );
      const { handlePosition, positionDeviation } = calculatedHandlePosition;
      const positionStyleObj = {
        top: "left",
        bottom: "left",
        left: "top",
        right: "top",
      };
      const handleStyle = {
        [positionStyleObj[handlePosition]]: `${positionDeviation}%`,
        opacity: 0,
      };

      const handleId = `${handleType}-${id}-${element.id}`;
      return (
        <Handle
          type={handleType}
          position={handlePosition}
          style={handleStyle}
          id={handleId}
          key={handleId}
        />
      );
    });
  };

getIncomers and getOutgoers and useStoreState are imported from react-flow-renderer. you must use a unique id for each node for this to work - if you look at the elements.filter function you will see that I am fetching the current element (if you have a better way please share)

Hope it helped 😃

1reaction
lomri123commented, Jul 23, 2021

@obaid @sebamont-nvx This will work for squared shapes:

const calculateHandlePosition = (firstNode, secondNode) => {
  // measurements
  const firstWidth = firstNode.__rf.width;
  const firstHeight = firstNode.__rf.height;
  const secondWidth = secondNode.__rf.width;
  const secondHeight = secondNode.__rf.height;

  // position
  const firstX = firstNode.__rf.position.x;
  const firstY = firstNode.__rf.position.y;
  const secondX = secondNode.__rf.position.x;
  const secondY = secondNode.__rf.position.y;

  let handlePosition = "top";
  let positionDeviation = 50;

  if (secondX - (firstX + firstWidth) > 0) {
    handlePosition = "right";
    if (firstY > secondY + secondHeight) {
      positionDeviation = 0; //from top
    } else if (secondY > firstY + firstHeight) {
      positionDeviation = 100; //from top
    } else {
      const centerSecond = secondY + secondHeight / 2;
      const topY = firstY - secondHeight / 2;
      const bottomY = firstY + firstHeight + secondHeight / 2;
      const rangeY = bottomY - topY;
      positionDeviation = (1 - (bottomY - centerSecond) / rangeY) * 100;
    }
    return { handlePosition, positionDeviation };
  } else if (firstX > secondX + secondWidth) {
    handlePosition = "left";
    if (firstY > secondY + secondHeight) {
      positionDeviation = 0; //from top
    } else if (secondY > firstY + firstHeight) {
      positionDeviation = 100; //from top
    } else {
      const centerSecond = secondY + secondHeight / 2;
      const topY = firstY - secondHeight / 2;
      const bottomY = firstY + firstHeight + secondHeight / 2;
      const rangeY = bottomY - topY;
      positionDeviation = (1 - (bottomY - centerSecond) / rangeY) * 100;
    }
    return { handlePosition, positionDeviation };
  } else {
    if (firstY > secondY - secondHeight) {
      handlePosition = "top";
    } else {
      handlePosition = "bottom";
    }
    if (firstX > secondX + secondWidth) {
      positionDeviation = 0; //from left
    } else if (secondX > firstX + firstWidth) {
      positionDeviation = 100; //from left
    } else {
      const centerSecond = secondX + secondWidth / 2;
      const right = firstX + firstWidth + secondWidth / 2;
      const left = firstX - secondWidth / 2;
      const rangeX = right - left;
      positionDeviation = ((centerSecond - left) / rangeX) * 100;
    }

    return { handlePosition, positionDeviation };
  }
};

export default calculateHandlePosition;

Read more comments on GitHub >

github_iconTop Results From Across the Web

Floating Edges Example - React Flow
This is an example implemenation for floating edges. The source and target position of the edges are getting calculated dynamically.
Read more >
DESIGN, ANALYSIS AND FIELD TEST OF A DYNAMIC ...
Abstract. Interest in floating breakwaters has been generated in recent years because the concept offers the potential of providing a less expensive ...
Read more >
Multi-Edge Server-Assisted Dynamic Federated Learning with ...
We propose cooperative edge-assisted dynamic federated learning (CE-FL). CE-FL introduces a distributed machine learning (ML) architecture, ...
Read more >
The Dynamic Floating Window - a new creative tool for 3D ...
Keywords: Dynamic Floating Window, DFW, floating window, 3D movies, ... defined by the enclosing four edges of the image, is known as the...
Read more >
Dynamic (Visible Edges) - Bentley - Product Documentation
Key-in: REFERENCE VISIBLEEDGES < ALLMODELS | ATTACHMENTS > DYNAMIC. Parent topic: Visible Edge Tools ... fit · fitted view · flag · flashbulb...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found