Is there any way to connect two dragable cards with a dynamic line segment (with symbols at end points) in ReactJs?

huangapple go评论78阅读模式
英文:

Is there any way to connect two dragable cards with a dynamic line segment (with symbols at end points) in ReactJs?

问题

我想要一个动态线段连接两个React rnd组件。线段的端点应该是符号。每当我移动这些组件时,线段也应该随之移动。如果第三个组件出现在线段之间,线段应该选择某条路线,避免与第三个或任何其他卡片相撞。

在ReactJS中能否创建这样的东西?

英文:

Is there any way to connect two dragable cards with a dynamic line segment (with symbols at end points) in ReactJs?

I want a dynamic line segment to connect two react rnd components. The line segment end points should be symbols. Also whenever I move these components the line segment should move with them and if in case a 3rd component comes in between the line segment, the segment should choose some route by having turns/curves avoiding the 3rd or any other card.

Is it possible to create such thing in reactJS?

答案1

得分: 1

以下是翻译好的内容:

  1. 有多种选项,其中一些比其他选项更混乱,但它们都基本上是“将线渲染成位于所有框架元素树更高位置的SVG元素”。

  2. “最简单”(但不“最直接”)的方法可能是有一个包含所有框的祖父组件,负责将线渲染成SVG。 它可能会提供用于addLine() / moveLine() / removeLine()的回调,这些回调会传递到元素层次结构中,并且每一对框都负责在适当时调用它们。

  3. 当然,用于渲染框的组件层次结构可能会相当混乱,而且属性传递并不好玩,所以也许作为上下文可能更好:

  4. 我建议采用一种不同的方法,它将线绘制的责任(和逻辑)保留在“BoxPair”组件内部,同时将“”呈现在元素层次结构中需要的位置,以便z-index叠放正常工作。

  5. 为了实现这一点,您可以使用portal。 Portal只是在元素层次结构的其他位置渲染组件树的一部分,位于当前组件之外。 您可以像以前一样创建SVG元素,每个“BoxPair”可以在元素树中的更高位置呈现其连接线:

  6. 在这里,出于简单起见,我直接将lineContainer元素引用作为属性传递,但像以前一样,您可以使用上下文。

英文:

From a comment (but not in the original question):
> whenever another cards come in between the two connecting cards, the line should take turns to avoid them

There are many options, some of them messier than others, but all of them are essentially "render the lines into an SVG element which is higher up the element tree than all the boxes".

The "simplest" (but not "most straightforward") approach would be to have a grandparent component that contains all your boxes and is responsible for rendering the lines into the SVG. It might provide callbacks to addLine() / moveLine() / removeLine() which get passed down the element hierarchy, and each pair of boxes is responsible for calling those as appropriate:

function LineContainer() {
    const addLine    = (x1, y1, x2, y2) => { /* some magic */ return id; }
    const removeLine = (id) => { /* remove line */ };
    /* ... */
    return (<>
      <svg>
        { lines.map(([x1,y1,x2,y2]) => <line x1={x1} y1={y1} ... /> }
      </svg>

      <WhateverRendersTheBoxes addLine={addLine} removeLine={removeLine} />
    </>)
}

function BoxPair({ addLine, removeLine }) {
    /* ... */

    useEffect(() => {
        const id = addLine({ x1, y1, x2, y2 });
        return removeLine(id);
    }, [x1, y1, x2, y2]);

    return <>
      <Box x={x1} y={y1} />
      <Box x={x2} y={y2} />
    </>
}

Of course, your component hierarchy for rendering the boxes might be pretty messy, and prop-drilling isn't a lot of fun, so maybe it would be better as a Context:

const LineContext = createContext();

function LineContainer() {
    /* ... same as above ... */
    return (<>
      <svg>
        { lines.map(([x1,y1,x2,y2]) => <line x1={x1} y1={y1} ... /> }
      </svg>

      <LineContext.Provider value={{ addLine, removeLine }}>
        <WhateverRendersTheBoxes />
      </LineContext.Provider>
    </>)
}

function BoxPair() {
    const { addLine, removeLine } = useContext(LineContext);
    /* ... same as above ... */
}

I would recommend a different approach which keeps the responsibility (and logic) for line drawing within the BoxPair component, while rendering the <line> wherever it needs to be in the element hierarchy for your z-index stacking to work.

To do this, you can use a portal. Portals simply render part of your component tree somewhere else in the element hierarchy, outside the current component. You could create your SVG element as before, and each BoxPair can render its connecting line higher up the element tree:

function LineContainer() {
    const [lineContainer, setLineContainer = useState();
    return (<>
      <svg ref={setLineContainer} />

      <WhateverRendersTheBoxes lineContainer={lineContainer} />
    </>);
}

function BoxPair({ lineContainer }) {
    /* ... */

    return <>
      <Box x={x1} y={y1} />
      <Box x={x2} y={y2} />

      { lineContainer &&
        createPortal(
          <line x1={pos1.x} y1={pos1.y} ... />,
          lineContainer
        ) }
    </>
}

Sandbox

Is there any way to connect two dragable cards with a dynamic line segment (with symbols at end points) in ReactJs?

For simplicity here I'm passing the lineContainer element reference directly as a prop, but as before you could use a context:

    <LineContainerContext.Provider value={lineContainer}>
      <WhateverRendersTheBoxes />
    </LineContainerContext.Provider>
...
    const lineContainer = useContext(LineContainerContext);

Sandbox

huangapple
  • 本文由 发表于 2023年4月11日 01:45:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/75979409.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定