2

I created a scene with multiple objects in the canvas element with ThreeJS. I now want to change the material color of an object if the user clicks on it.

enter image description here

Excerpt of my Angular 5 component, which holds all the ThreeJS content:

public onMouseDown(event: MouseEvent) {
    console.log("onMouseDown");
    event.preventDefault();

    var rayCaster = new THREE.Raycaster();
    var mouse = new THREE.Vector2();

    mouse.x = (event.clientX / this.renderer.domElement.clientWidth) * 2 - 1;
    mouse.y = -(event.clientY / this.renderer.domElement.clientHeight) * 2 + 1;

    rayCaster.setFromCamera(mouse, this.camera);

    var intersects = rayCaster.intersectObjects(this.scene.children);

    console.log("Scene has " + obj.length + " objects");
    console.log(intersects.length + " intersected objects found");

    intersects.forEach((i) => {
      i.object.material =
        new THREE.MeshBasicMaterial({color: 0xf1f11f});
    });
    this.render();
}

The problem is that the mouse coordinates are not correct, due to the distance between canvas element and the document outer edge. Whenever I click in the scene, an object right of the mouse will get the new material color.

Some information: this.renderer.domElement.clientWidth holds the width of the canvas element

How to calculate the correct mouse coordinates if the canvas element isn't full screen?

d4rty
  • 3,970
  • 5
  • 34
  • 73
  • This is less a three.js question and more a JavaScript/layout question. – TheJim01 Jan 22 '18 at 20:46
  • 1
    Related: see https://stackoverflow.com/questions/13542175/three-js-ray-intersect-fails-by-adding-div/13544277#13544277 Also, don't instantiate new Raycater, Vector2, Material unnecessarily. – WestLangley Jan 22 '18 at 21:38
  • @WestLangley How to use Raycaster without instantiating one? You mean to have a global Raycaster object instead of a local object inside the function? – d4rty Jan 23 '18 at 08:19
  • Yes, or use a closure, so a single Raycaster instance can be reused. Change the color of an existing material; do not assign a new material. – WestLangley Jan 23 '18 at 15:22

1 Answers1

2

If your flow is broken (your container is floated or manually positioned), then clientX/clientY will report "bad" values (relative to the page, rather than to the container like you might expect). The properties offsetX/offsetY account for this by reporting values relative to the container on which the event was fired.

According to MDN, support is limited, but my testing just now (01/22/2018) showed the latest versions of Chrome, Firefox, Edge, and IE all reported the correct values*. Just note that some browsers report integer values, while others may report double values.

*This may not hold true for Safari or mobile browsers. YMMV.

TheJim01
  • 8,411
  • 1
  • 30
  • 54