Eliav Louski
Eliav Louski

Reputation: 5274

react drag,drop, and connect arrow(or anything else) with animation between elements

after few days of trying with no success, I came here (again..), to all of you experts.

first of all: live demo! because we all love live demos.

Codesandbox

https://codesandbox.io/s/drag-with-not-animation-b3eh7?file=/src/App.js

I'm trying to make interactive draggable and dropable arrows between containers(means - there is a connector to a box, you can drag your mouse from one of the containers to another box and an arrow between them will be created).

NEVER THEM BOTH! HELP! ;<

more detailed explanations inside the code.

what I've already tried:

Upvotes: 5

Views: 8206

Answers (1)

Vivek Doshi
Vivek Doshi

Reputation: 58573

Here you go :

To draw a Xarrow you need a starting point and ending point

  • Start point : will always be dragging from
  • End Point : Where you are dropping

Here We have 2 refs ,

  • ref0 : Starting drag point ( which will be the box )
  • ref1 : Draggable point ( Will be the draggable point )

Here is the code that needs to be changed, please do read the comments also, that will make the flow clear.

const ConnectPointsWrapper = ({ boxId, handler, ref0 }) => {
    const ref1 = useRef();

    const [position, setPosition] = useState({});
    const [beingDragged, setBeingDragged] = useState(false);
    return (
        <React.Fragment>
            <div
                className="connectPoint"
                ref={ref1} // <---- referencing the point
                style={{
                    ...connectPointStyle,
                    ...connectPointOffset[handler],
                    ...position // <----- Updating the position as we drags
                }}
                draggable
                onDragStart={e => {
                    setBeingDragged(true);
                    e.dataTransfer.setData("arrow", boxId);
                }}
                onDrag={e => {
                    setPosition({ // <---- Setting up the position to draw line as we drags
                        position: "fixed",
                        left: e.clientX,
                        top: e.clientY,
                        transform: "none",
                        opacity: 0
                    });
                }}
                onDragEnd={e => {
                    setPosition({});
                    setBeingDragged(false);
                }}
            />
            {beingDragged ? <Xarrow start={ref0} end={ref1} /> : null} // <---- this will draw the arrow b/w ref0 and ref1
        </React.Fragment>
    );
};

const Box = ({ text, handler, addArrow, boxId }) => {
    const ref0 = useRef(); 
    return (
        <div
            id={boxId}
            style={boxStyle}
            ref={ref0} // <---- referencing the box it self
            onDragOver={e => e.preventDefault()}
            onDrop={e => {
                if (e.dataTransfer.getData("arrow") === boxId) {
                    console.log(e.dataTransfer.getData("arrow"), boxId);
                } else {
                    const refs = { start: e.dataTransfer.getData("arrow"), end: boxId };
                    addArrow(refs);
                }
            }}
        >
            {text}
            <ConnectPointsWrapper {...{ boxId, handler, ref0 }} /> // <---- Passing the `ref0` to `ConnectPointsWrapper` to draw line from point
        </div>
    );
};

WORKING DEMO :

Edit #SO-drag-anim-done


NOTE :

I was trying to set style just via ref1 and not with setPosition, you can check the below code snippet for that,

<div
        className="connectPoint"
        style={{
          ...connectPointStyle,
          ...connectPointOffset[handler]
        }}
        draggable
        onDragStart={e => {
          setBeingDragged(true);
          e.dataTransfer.setData("arrow", boxId);
        }}
        onDrag={e => {
          setPosition({}); // <---- just to force re-rendering, to draw arrow with updated value
          ref1.current.style.position = "fixed";
          ref1.current.style.left = e.clientX + "px";
          ref1.current.style.top = e.clientY + "px";
          ref1.current.style.transform = "none";
          ref1.current.style.opacity = 0;
        }}
        ref={ref1}
        onDragEnd={e => {
          ref1.current.style.position = "absolute";
          ref1.current.style.left = connectPointOffset[handler].left;
          ref1.current.style.top = connectPointOffset[handler].top;
          ref1.current.style.transform = connectPointOffset[handler].transform;
          ref1.current.style.opacity = 0.5;
          setBeingDragged(false);
        }}
      />

WORKING DEMO : ( this is just another way )

Edit
#SO-drag-anim-optimization


EDIT :

WORKING DEMO : ( With draggable box also )

Edit drag-anim-with-draggable

Upvotes: 13

Related Questions