Mouseover doesn't work properly compared to Click event in Threejs

Viewed 751

I’m kind of new to Three.js

I’m trying to load GLTF model, add mouseover and mouseout events, where first will change the GLTF color, while the second will revert it back to the original.

So far I have been able to achieve it - partially, as it appear in this video, it doesn’t change anything, unless the mouse pointer come from (out of focus maybe?) directly into the GLTF model without going through the black background.

The code I’m using:

const loader = new GLTFLoader();
loader.load('models/box.glb', function (gltf) {
    gltf.scene.traverse(function (child) {
        if (child.isMesh) {
            let m = child;
            m.receiveShadow = true;
            m.castShadow = true;
            m.material.flatShading = true;
            sceneMeshes.push(m);
        }
        if (child.isLight) {
            let l = child;
            l.castShadow = true;
            l.shadow.bias = -.003;
            l.shadow.mapSize.width = 2048;
            l.shadow.mapSize.height = 2048;
        }
    });
    scene.add(gltf.scene);
    console.log(gltf.scene);
}, (xhr) => {
    console.log((xhr.loaded / xhr.total * 100) + '% loaded');
}, (error) => {
    console.log(error);
});
const raycaster = new THREE.Raycaster();
const sceneMeshes = new Array();
renderer.domElement.addEventListener('mouseover', onMouseMove, false);
renderer.domElement.addEventListener('mouseout', onMouseOut, false);
function onMouseMove(event) {
    const mouse = {
        x: (event.clientX / renderer.domElement.clientWidth) * 2 - 1,
        y: -(event.clientY / renderer.domElement.clientHeight) * 2 + 1
    };
    raycaster.setFromCamera(mouse, camera);
    const intersects = raycaster.intersectObjects(sceneMeshes, false);
    if (intersects.length > 0) {
        intersects[0].object.material.color.set(0xffff00);
    }
}
function onMouseOut(event) {
    const mouse = {
        x: (event.clientX / renderer.domElement.clientWidth) * 2 - 1,
        y: -(event.clientY / renderer.domElement.clientHeight) * 2 + 1
    };
    raycaster.setFromCamera(mouse, camera);
    const intersects = raycaster.intersectObjects(sceneMeshes, false);
    if (intersects.length > 0) {
        intersects[0].object.material.color.set(0xff0000);
    }
}

Any idea what i might be done wrong?

2 Answers

Try it with this simplified code:

let camera, scene, renderer, mesh;
let raycaster, mouse;

let currentIntersection = null;

init();
animate();

function init() {

  camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10);
  camera.position.z = 4;

  raycaster = new THREE.Raycaster();
  mouse = new THREE.Vector2();

  scene = new THREE.Scene();

  const geometry = new THREE.BoxBufferGeometry();
  const material = new THREE.MeshBasicMaterial( { color: 0xff0000 } );

  mesh = new THREE.Mesh(geometry, material);
  scene.add(mesh);

  renderer = new THREE.WebGLRenderer({
    antialias: true
  });
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);

  renderer.domElement.addEventListener('pointermove', onMouseMove, false);

}

function animate() {

  requestAnimationFrame(animate);

  mesh.rotation.x += 0.01;
  mesh.rotation.y += 0.02;

  renderer.render(scene, camera);

}

function onMouseMove(event) {
  mouse.x = (event.clientX / renderer.domElement.clientWidth) * 2 - 1;
  mouse.y = -(event.clientY / renderer.domElement.clientHeight) * 2 + 1;
  raycaster.setFromCamera(mouse, camera);
  const intersects = raycaster.intersectObject(scene, true);
  if (intersects.length > 0) {
    currentIntersection = intersects[0].object;
    currentIntersection.material.color.set(0xffff00);
  } else {
    if (currentIntersection !== null) {
      currentIntersection.material.color.set(0xff0000);
      currentIntersection = null;
    }
  }
}
body {
  margin: 0;
}

canvas {
  display: block;
}
<script src="https://cdn.jsdelivr.net/npm/three@0.120.1/build/three.js"></script>

You normally only need a pointermove event listener to implement a "hover" effect.

I got it solved by using mousemove event, and the following code inside onMouseMove event:

if (intersects.length > 0) {
    INTERSECTED = intersects[0].object;

    if (intersects[0].object) {
        INTERSECTED.material.color.set(0xff0000);
        console.log("Touching")
    }
        
} else {
    INTERSECTED.material.color.set(0xfff000);
    console.log("Not touching")
}

Basically i was using the else statement in the wrong place - right after the if (intersects[0].object)

Related