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
- 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.
- 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)
- 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",