Skip to content

Pointer capture does not work in react-native #3315

Open
@krzys-h

Description

@krzys-h

I'm trying to implement a drag-and-drop like feature using R3F event system. It works perfectly on the web, but in react-native builds, trying to call setPointerCapture / releasePointerCapture as described in the docs (https://docs.pmnd.rs/react-three-fiber/api/events#pointer-capture) fails. Without it, onMouseMove / onMouseUp don't get properly delivered if you move the pointer too fast and it leaves the object.

Specifically, the code fails here:
https://github.com/pmndrs/react-three-fiber/blob/v8.16.8/packages/fiber/src/core/events.ts#L297
https://github.com/pmndrs/react-three-fiber/blob/v8.16.8/packages/fiber/src/core/events.ts#L150

on react-native, the event.target field seems to be an integer handle of the target view, not an object, so event.target.setPointerCapture does not exist.

For testing, I patched it out like this:

diff --git a/node_modules/@react-three/fiber/dist/index-99983b2d.esm.js b/node_modules/@react-three/fiber/dist/index-99983b2d.esm.js
index d3aa0a8..606b519 100644
--- a/node_modules/@react-three/fiber/dist/index-99983b2d.esm.js
+++ b/node_modules/@react-three/fiber/dist/index-99983b2d.esm.js
@@ -835,7 +835,7 @@ function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
     // If this was the last capturing object for this pointer
     if (captures.size === 0) {
       capturedMap.delete(pointerId);
-      captureData.target.releasePointerCapture(pointerId);
+      if(captureData.target.releasePointerCapture) captureData.target.releasePointerCapture(pointerId);
     }
   }
 }
@@ -991,7 +991,7 @@ function createEvents(store) {
             // faster access.
             internal.capturedMap.set(id, new Map([[hit.eventObject, captureData]]));
           }
-          event.target.setPointerCapture(id);
+          if(event.target.setPointerCapture) event.target.setPointerCapture(id);
         };
         const releasePointerCapture = id => {
           const captures = internal.capturedMap.get(id);

With that change, pointer capture still didn't work, because on react-native e.pointerId is called e.identifier. This caused it to silently fail here:
https://github.com/pmndrs/react-three-fiber/blob/v8.16.8/packages/fiber/src/core/events.ts#L257-L258

so as an additional hack I did this:

if (typeof event.pointerId === 'undefined')
    event.pointerId = event.identifier;

and now the pointer capture seems to work.

I would have expected this to work only partially, and break if you move the pointer out of the canvas while holding the object, but it seems that at least in my setup, react-native defaults to capturing the pointer to the view you initially clicked on. This could be some side-effect of using r3f-native-orbitcontrols though, I didn't fully test it. The "proper" way to implement it in react-native is probably to use PanResponder, which I can see is already partially done in #3252.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingreact-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