react-three-fiber: onPointerOut doesn't fire when moving to an abutting object
I made a sandbox to demonstrate the differences between R3F pointer events and native DOM. (The ts-ignore
in the sandbox are there because onPointerEnter
and onPointerLeave
seem to be missing from the EventHandlers
type in types.ts
: I can make a PR to add that if that’s helpful.)
To see the DOM event behaviour, move the mouse onto the A at the top, then from A directly to B (there’s no gap between them). The move from A to B gives these events:
DOM out A
DOM child leave A
DOM child enter B
DOM over B
You can see the equivalent situation in R3F below. Move your pointer onto the red cube, then directly onto the blue cube. Again, there’s no gap between them. When you move from red to blue, you get these events:
R3F child enter bravo
R3F child leave alpha
Note that the leave and enter are the opposite way around compared to the native events; more on this later. More importantly, note the absence of the “out” event. This comes later, when you move out from the blue cube onto empty space:
R3F out alpha
R3F leave alpha
R3F child leave bravo
The top-level leave event is on the wrong element: it should be bravo, not alpha. And the out event only comes now. Bravo (the blue cube) never got an over or out event.
Looking at canvas.tsx, the order is clearly because handlePointerCancel
only happens at the end of the event processing, and I think the lack of events on bravo might be because this line is comparing the eventObject
instead of the object
. Using the eventObject
would be correct for enter/leave, where you’re not supposed to get events for boundaries inside the element that has the event handler, but you are supposed to get them for over/out, so I think it needs to compare object
instead.
It might be worth noting that I only tried using onPointerOver/Out because of the reverse order of onPointerEnter/Leave. In my application, I need to handle these events by setting a state that contains the currently hovered object, or empty if no object is hovered. First I tried to use an onPointerEnter/Leave handler on the child objects, but then I got a bug with overlapping objects, because the late onPointerLeave would clear my hovered object state after it was set to the new object. I checked the W3C specs for pointer and mouse events and couldn’t find anything saying which order they should be in, so I don’t know if the behaviour is wrong (from the POV of not matching the specified behaviour of native events), but it’s definitely inconvenient this way around. I tried using onPointerOver/Out on my root node instead as a workaround, but then hit this bug.
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Comments: 37 (37 by maintainers)
published a new patch version: 5.1.1
my testcase: https://codesandbox.io/s/pointeroverorder-r3f-forked-nkvii?file=/src/App.js
red > green > red > out, yields …
Boxred pointerOver Boxred pointerOut Boxgreen pointerOver Boxgreen pointerOut Boxred pointerOver Boxred pointerOut
and without stoppropagation:
Boxred pointerOver Boxgreen pointerOver Boxred pointerOut Boxred pointerOver Boxgreen pointerOut Boxred pointerOut
both seem correct.
thanks everyone, so glad this is fixed!