react-konva icon indicating copy to clipboard operation
react-konva copied to clipboard

How to make the inner shape clickable when 2 shapes are overlapping : React Konva

Open nnha19 opened this issue 1 year ago • 5 comments

here is my issue. We are using React Konva to allow users to draw 3 shapes. Rectangle, Ellipse and Polygon. Users can draw as many shapes as they want, even the shapes that overlap. So, when 2 or more shapes are overlapping, users should still be able to click and drag the shapes under the overlapping shapes. Right now, the shape that was drawn last stays on top. So, if there is any other shape under that last drawn shape, we can't do anything to those shapes underneath like hovering and clicking. Is rearranging the order of the shapes inside an array the only solution? Is there any other straightforward solution for that?? This is how my shapes object look like.

shapes : {
 rectangles :[{}],
 ellipses :[{}],
 polygon :[{}]
}

For better demonstration https://codesandbox.io/s/shape-overlay-rect-konva-2c4wg7?file=/src/Canvas/Canvas.jsx

Thanks in advance

nnha19 avatar Jul 23 '22 10:07 nnha19

It is better not to post at all possible channels. You already have SO post: https://stackoverflow.com/questions/73089086/how-to-make-the-inner-shape-clickable-when-2-shapes-are-overlapping-react-konv

I have no idea what kind of UX you are looking for. All shapes are clickable and hoverable at the same time? You can enable interactions only via strokes, by disabling filling part.

lavrton avatar Jul 26 '22 04:07 lavrton

Hmm. Sorry if it bothers you. Just thought it would improve my chance of getting answer from someone if I have it in several places. Yes, I want the shapes hoverable and clickable at the same time. That is the requirement and since we have too many shapes and also different kind of shapes, changing the order manually is not helpful either. I have tried getting the intersecting shapes and moving the other ones to top by using moveToTop(). It works for a lot of situations, but in some cases when only half of the shape is overlapping, it doesn't work.

const handleOverlayShapes = (
  { target }: KonvaEventObject<MouseEvent>,
  shapesState: ShapesReducerType,
  stageRef: React.RefObject<Stage>,
) => {
  // Making overlapped shapes hoverable and clickable. Doesn't work in some cases.
  const allShapes = [
    shapesState.rectangles,
    shapesState.ellipses,
    shapesState.polygons,
  ]
    .flat()
    .filter((s) => s.id !== target.attrs.id);
  if ('intersects' in target) {
    allShapes.forEach((shape) => {
      if ('x' in shape) {
        const isIntersecting = target.intersects({ x: shape.x, y: shape.y });
        if (isIntersecting) {
          const intersectingNode = stageRef.current?.find(`#${shape.id}`)[0];
          if (!intersectingNode) return;
          // For rectangle and ellipse, transformer has to be moved as well.
          const shapeTransformer = stageRef.current?.find(
            `#transformer-${shape.id}`,
          )[0];
          if (shapeTransformer) shapeTransformer.moveToTop();
          intersectingNode.moveToTop();
        }
      } else if ('points' in shape) {
        // hovered shape intersecting with polygon
        const isIntersecting = shape.points.some(([x, y]) =>
          target.intersects({ x, y }),
        );
        if (isIntersecting) {
          const intersectingNode = stageRef.current?.find(`#${shape.id}`)[0];
          if (!intersectingNode) return;
          intersectingNode.moveToTop();
        }
      }
    });
  }
};

Thank you very much

nnha19 avatar Jul 26 '22 04:07 nnha19

I still don't understand how you see it working. Let's think that we have this:

image

Two overlapping shapes. Mouse is on overlapping parts. We hover here and click. What kind of events do you need? Hover and click on both of them? If so, why do you need it? What kind of interaction you are doing? That is not the way most apps are working.

lavrton avatar Jul 26 '22 15:07 lavrton

No, that is not exactly what I need. I don't need events to get fired simultaneously for both of them. For the shapes you shared above, it's ok as it is. Because they are not entirely overlapping. I have cases like one ellipse is completely inside bigger ellipse. In that case, I want to make it so that the inner one is hoverable and clickable. In other word smaller one should always have higher z-index so that one cursor is over the smaller one, events should get fired for that smaller shape. And that should work for any sorts of shapes in anyway overlapping.

overlapping-shapes

For the picture above, users should be able to hover for all the shapes regardless of the order they were drawn in. Could you be able to point me to get that functionality working, please??

Thanks

nnha19 avatar Jul 26 '22 15:07 nnha19

Then it is up to you to set the correct order. Konva can't magically understand what shape you want to have clicked. Every time a user is added a new shape and change it, you need to do your own math, find what order you prefer and set it. In most of the apps, zIndex is defined by order of creating shapes. The last added shape is on top. And there are special buttons to change that order.

My example is simple, but from it you should deeply understand what kind of UX you are looking for. What shapes are clickable in this cases:

image

You may have to do a lot of math by yourself if you want to have automatic change order. But from my point of view, it is a very strange behavior of app. Will your users understand why a shape move to top when they changed something.

lavrton avatar Jul 26 '22 15:07 lavrton