Skip to content

react-native gesture responders can't be nested #3332

Open
@ashkalor

Description

Problem

In react native once a view becomes a Gesture Responder, canvas elements stop gettings events. We can use events on canvas elements as long as no other view encompassing the canvas becomes a Gesture Responder.

What would really help in this situation.

Prioritize events for canvas elements and only set onStartShouldSetResponder and onMoveShouldSetResponder when meshes dont have a hit i.e onPointerMissed, this way we can interact with the 3d scene easily and only when pointers are missed we can choose to use libraries like
r3f-orbit-controls

Current Work around

So right now I am able to get some semblance of this working by doing what I have shared below, however this has a major drawback
that one touch event is lost in trying to set GestureResponder to start.

const startShouldSetResponder = useRef<boolean>(false);
  const moveShouldSetResponder = useRef<boolean>(false);
  const [OrbitControls, events] = useControls();
  return (
    <Canvas
      style={{
        flex: 1,
      }}
      onLayout={events.onLayout}
      onPointerMissed={(event) => {
        console.log("Pointer Missed");
        startShouldSetResponder.current = true;
        moveShouldSetResponder.current = true;
      }}
      onResponderRelease={() => {
        console.log("Canvas click");

        events.onResponderRelease();
        startShouldSetResponder.current = false;
        moveShouldSetResponder.current = false;
      }}
      onResponderMove={(event) => {
        console.log("Canvas move");
        events.onResponderMove(event);
      }}
      onStartShouldSetResponder={(event) => {
        events.onStartShouldSetResponder(event);
        return startShouldSetResponder.current;
      }}
      onMoveShouldSetResponder={(event) => {
        events.onMoveShouldSetResponder(event);
        return moveShouldSetResponder.current;
      }}
    >
      <OrbitControls />
      <ambientLight intensity={0.1} />
      <directionalLight color="red" position={[0, 0, 5]} />
      <mesh
        onClick={() => {
          console.log("Mesh Click");
        }}
      >
        <boxGeometry />
        <meshStandardMaterial />
      </mesh>
    </Canvas>
  );

Possible Solutions

  1. Get GestureResponder event in onPointerMissed so that we can use that event and call the different callbacks without losing that one touch event so that we can start using orbit controls directly.
  2. Create a fake GestureResponderEvent from MouseEvent and do what i mentioned above however I had limited success in getting that to work (Maybe I have knowledge gaps that is causing me to miss something very obvious)
  3. Use an Event State Machine for Native Events as trying to handle propagation and bubbling effectively in native might not be the best idea, inspired from this video by Andy Matuschak. For this i think integrating with React native gesture handler might be extremely fruitful
    as they seem to handle these cases very well. (Currently using GestureDetector from this library inside canvas causes the app to crash)

And Finally I would really like to thank @CodyJasonBennett, @TiagoCavalcante and other contributors in this ecosystem for all the work these guys put in for making something like this even remotely possible for react native.

Packages used

 "@react-three/drei": "^9.109.5",
 "@react-three/fiber": "^8.17.5",
 "r3f-native-orbitcontrols": "^1.0.12",
 "react-native-gesture-handler": "~2.16.1",

Metadata

Assignees

No one assigned

    Labels

    needs more infoneeds more information before we can actionreact-nativeto do with react-native

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions