Finn Formica
Finn Formica

Reputation: 138

How to stop React Three canvas elements from receiving an onClick event when behind another element in the 3D space?

Context

Using React Three Fiber and Drei I have created a simple rotating gallery of images to create a 3D carousel of projects.

image of the react three gallery of images

I have set up an onClick event to fire on each of the cards as shown below, which passes down the handleClick func to the <Image /> component from react three drei:

function Carousel({ cards, handleClick }: CarouselProps) {
  const count = cards.length;
  const radius = (count * 2.5) / (Math.PI * 2);

  return cards.map((card, i) => (
    <Card
      key={i}
      card={card}
      onClick={() => handleClick(i)}
      position={[
        Math.sin((i / count) * Math.PI * 2) * radius,
        0,
        Math.cos((i / count) * Math.PI * 2) * radius,
      ]}
      rotation={[0, (i / count) * Math.PI * 2, 0]}
    />
  ));
}

A working demo can be seen here.

The code for the 3D carousel can be found on here, along with the card component.

Problem

When clicking the image the onClick func is called as intended, however, when two images are overlapping (i.e. one is behind the other in the circle) then the onClick func is called twice - once for the image in front and then immediately after once for the image behind. This only happens if the mouse happens to intersect with both images (although the one behind cannot be seen).

Failed Solution

I've attempted throttling and debouncing the calls but the second call is just made after the delay set by the functions.

I'm sure there's either a native Javascript solution to this or something within React Three to handle this kind of situation but I have been struggling to find this solutions myself.

Upvotes: 1

Views: 31

Answers (1)

Finn Formica
Finn Formica

Reputation: 138

Turns out throttle() from lodash was the correct way to go. To solve this problem I ensured that the options for the trailing edge were set to false:

const handleClick = _.throttle(
    (index: number) => {
      if (active === undefined) {
        setActive(index);
        return;
      }

      setActive(undefined);
    },
    200,
    { trailing: false },
  );

Upvotes: 1

Related Questions