0

Yo guys, I cannot figure how to make a connected line between two circles being dragged along with one of a circle. I guess one way is to update the actual coordinate of a circle being dragged, but I am struggling to implement that as well. Can anyone help me pls???

import React from "react";
import { render } from "react-dom";
import { Stage, Layer, Star, Line, Text } from "react-konva";

function generateShapes() {
  return [...Array(10)].map((_, i) => ({
    id: i.toString(),
    x: Math.random() * window.innerWidth,
    y: Math.random() * window.innerHeight,
    rotation: Math.random() * 180,
    isDragging: false
  }));
}

const INITIAL_STATE = generateShapes();

const App = () => {
  const [stars, setStars] = React.useState(INITIAL_STATE);

  const [connectors, setConnectors] = React.useState([]);
  const [fromShapeId, setFromShapeId] = React.useState(null);

  const handleDragStart = (e) => {
    const id = e.target.id();
    setStars(
      stars.map((star) => {
        return {
          ...star,
          isDragging: star.id === id
        };
      })
    );
  };
  const handleDragEnd = (e) => {
    setStars(
      stars.map((star) => {
        return {
          ...star,
          isDragging: false
          // update coordinate of the star
        };
      })
    );
  };

  return (
    <Stage width={window.innerWidth} height={window.innerHeight}>
      <Layer>
        {connectors.map((con) => {
          const from = stars.find((s) => s.id === con.from);
          const to = stars.find((s) => s.id === con.to);

          return (
            <Line
              key={con.id}
              points={[from.x, from.y, to.x, to.y]}
              stroke="black"
            />
          );
        })}

        <Text text="Try to drag a star" />
        {stars.map((star) => (
          <Star
            key={star.id}
            id={star.id}
            x={star.x}
            y={star.y}
            numPoints={5}
            innerRadius={20}
            outerRadius={40}
            fill={fromShapeId === star.id ? "red" : "green"}
            opacity={0.8}
            draggable
            rotation={star.rotation}
            shadowColor="black"
            shadowBlur={10}
            shadowOpacity={0.6}
            shadowOffsetX={star.isDragging ? 10 : 5}
            shadowOffsetY={star.isDragging ? 10 : 5}
            scaleX={star.isDragging ? 1.2 : 1}
            scaleY={star.isDragging ? 1.2 : 1}
            onDragStart={handleDragStart}
            onDragEnd={handleDragEnd}
            onClick={() => {
              if (fromShapeId) {
                const newConnector = {
                  from: fromShapeId,
                  to: star.id,
                  id: connectors.length
                };
                setConnectors(connectors.concat([newConnector]));
                setFromShapeId(null);
              } else {
                setFromShapeId(star.id);
              }
            }}
          />
        ))}
      </Layer>
    </Stage>
  );
};

render(<App />, document.getElementById("root"));

My first attempt is to create a function that detects the position of the star when it stops being dragged by using var x = e.clientX and var y = e.clientY. However, both of them are undefined. I think maybe I have to create some sort of array that stores the coordinates of each star and updates a coordinate of that star when being moved. Nonetheless, I really don't know how to implement the code :/

BOB
  • 1
  • 3

1 Answers1

0

detects the position of the star when it stops being dragged by using var x = e.clientX and var y = e.clientY

The e variable is a Konva wrapper around the native DOM event. You can access the underlying event by using e.evt.clientX (similar to how you would use e.nativeEvent.clientX on a React synthetic event).

You can also get the coordinates from the Star object by using e.target.x().


I don't think that onClick is the right event to be using when you are dragging the stars. I modified your code and moved the setConnectors call from onClick into the onDragEnd handler. I've got it working so that it creates a continuous line through each dragged star. Dragging a star and dropping it causes a line to appear between that star and the last one in the line.

const handleDragEnd = (e) => {
  setStars(
    stars.map((star) => {
      return {
        ...star,
        isDragging: false,
        // update coordinates of the active star only
        x: star.isDragging ? e.evt.x : star.x,
        y: star.isDragging ? e.evt.y : star.y
      };
    })
  );
  const id = e.target.id();
  if (fromShapeId && fromShapeId !== id) {
    const newConnector = {
      from: fromShapeId,
      to: id,
      id: connectors.length
    };
    setConnectors(connectors.concat([newConnector]));
  }
  setFromShapeId(id);
};

CodeSandbox Demo

Linda Paiste
  • 38,446
  • 6
  • 64
  • 102
  • Thank u very much!! greatly appreciated :) !!!, btw how to implement a way to delete the connector between two stars??? – BOB Aug 14 '21 at 11:44
  • What’s the actual end goal here? I wasn’t sure when you wanted to draw the line. What action triggers deleting a line? – Linda Paiste Aug 14 '21 at 14:33
  • Sorry for confusing u, I have done some modification on your code: https://codesandbox.io/s/boring-lederberg-5szvx?file=/src/App.js . I am struggling to create a function where the connector between two circles is removed as soon as two circles are clicked twice with a connector. Inside the code sandbox, I have highlighted all problems. – BOB Aug 15 '21 at 10:15