Description
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.