Hello, I have the following code which works great to “hover” objects, however it is possible for multiple objects to be hovered at once if their meshes overlap at the point of the mouse. I understand that the intersections array is sorted such that the first element is the closest, but I can’t figure out how to use this to my advantage given the following code. Any help is appreciated.
const raycaster = new THREE.Raycaster();
const pointer = new THREE.Vector2();
var raycastLayer = [];
let hovered = {};
let intersects = [];
window.addEventListener('pointermove', (e) => {
pointer.set((e.clientX / window.innerWidth) * 2 - 1, -(e.clientY / window.innerHeight) * 2 + 1)
raycaster.setFromCamera(pointer, camera)
intersects = raycaster.intersectObjects(raycastLayer, true)
// if a previously hovered item is not among the hits we must call onPointerOut
Object.keys(hovered).forEach((key) => {
const hit = intersects.find((hit) => hit.object.uuid === key)
if (hit === undefined) {
const hoveredItem = hovered[key]
if (hoveredItem.object.onPointerOver) hoveredItem.object.onPointerOut(hoveredItem)
delete hovered[key]
}
})
intersects.forEach((hit) => {
// if a hit has not been flagged as hovered we must call onPointerOver
if (!hovered[hit.object.uuid]) {
hovered[hit.object.uuid] = hit
if (hit.object.onPointerOver) hit.object.onPointerOver(hit)
}
// call onPointerMove
if (hit.object.onPointerMove) hit.object.onPointerMove(hit)
})
render();
})
For a super-quick fix without much changes, you should be able to just change this line:
intersects.forEach((hit) => {
into:
[ intersects[0] ].forEach((hit) => {
And you’re done
(If you’d prefer the longer, and also a bit cleaner way, all you need to do is to remove .forEach entirely, and just work with intersects[0] - no loops involved. Just check if value of hovered is the same as intersects[0], call onPointerOut if it’s not, then call onPointerOver for intersects[0], set hovered to intersects[0].)
Thanks for your help. I’ve been stuck on this for a couple of hours and tried a bunch of different things, to no avail! I’m sure it’s very simple if someone is able to take a glance and help me out:
I realize I am still using the full array for the hovered section, but whatever I did before didn’t work so I just put it back.
you essentially want something like the doms event.stopPropagation() which requires event bubbling. just picking the first imo is not enough and will result in buggy behaviour. this all gets worse and worse and worse with having to manage pointerOut, capture once the pointer goes out the window etc. if you want, here’s a full pointer event layer: react-three-fiber/events.ts at 75521d21511b3523dbd8f692af8211710f036006 · pmndrs/react-three-fiber · GitHub but imo it makes no sense to re-implement this in vanilla, just use three with react where all of this is battle tested and taken care of.