Search code examples
konvajsreact-konva

How do I move a group of objects and keep their new position in state?


I'm new to React Konva and trying to wrap my head around position.

I have a relatively simple use case; a Group containing a Rect and some Lines. I want to be able to drag and drop the Group as a whole, and capture the new position on drag end.

I am attempting to do this using onDragEnd on the Group to set state, based on e.target.x() and e.target.y(), but this gives some behaviour that I don't understand (orange Rect in sandbox).

Codesandbox

How can I get the behaviour I want (blue Rect) whilst getting the new position?


Solution

  • Positions of all Konva nodes are relative to parent(s). Every node has its own x and y position.

    When you are dragging a group, the position of the GROUP is changed. It means actual x and y attributes of the group instance. Even if you don't have defined x and y of the group explicitly, they still exist.

    The absolute position of the rectangle on the canvas is defined by its own position and the positions of all parts. Like

    Absolute Position of Rectangle = Position of stage + position of layer + position of group + position of rectangle.

    This is your onDragEnd handler:

    <Group
      draggable
      onDragEnd={(e) => {
        setRectPos({
          x: e.target.x(),
          y: e.target.y(),
        });
      }}
    >
    

    In this code, e.target - is the group instance. So using its position for rectangle position doesn't make much sense and this is why you have such jumps. Because position change is applied twice: (1) by dragging and (2) by updating state.

    The better approach by be this:

    const [rectPos, setRectPos] = useState({ x: 100, y: 100 });
    
    <Group
      draggable
      x={rectPos.x}
      y={rectPos.y}
      onDragEnd={(e) => {
        setRectPos({
          x: e.target.x(),
          y: e.target.y(),
        });
      }}
    >
      <Rect width={100} height={100} fill="orange" />
      <Line
        lineCap={"round"}
        x={50}
        y={50}
        points={[0, 0, 0, 0]}
        strokeWidth={10}
        stroke={"green"}
      />
    </Group>