I need to know if my mouse clicks on an object in a 3D Three.JS scene.

The way I'm going to build it is going to require a few things:

  • A mouse.

  • A raycaster. This sends lines across the scene to see if the linear click through the scene hits anything.

  • An object to check against.

  • A DOM element (canvas, body, whatever) to click into. I mention this one because we need to attach the click event to it.

Examples for reference:

Three.JS example with hover over objects working.

Three.JS example with interactive cubes.

Step 1: Make these objects

I'm not going to tell you how to make an object in a 3D scene, but you should be able to figure it out if you followed my Basic Three.JS Scene Instructions. This is my attempt to keep the wiki DRY/get away with being lazy...

We still need the mouse and raytracer. I add them as properties to my Visualizer object.

In my Visualizer object, I added:

this.initMouse = () => {
  this.raycaster = new THREE.Raycaster()
  this.mouse = new THREE.Vector2()
}

and then I called this.initMouse in its initialize method.

Great!

Step 2: Make an event listener for clicks

We can just listen to any click on the page for now.

this.initEventListeners = () => {
  const onDocumentMouseDown = (event) => {
    // Set mouse params
    event.preventDefault();
    this.mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
    this.mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
  }
  document.addEventListener( 'mousedown', onDocumentMouseDown, false )

Step 3: Check if the raycast from the mouse click intersects the object

I'll just pretend that I know what that means. Our click creates a line that dives into the page. This is a line (ray) sent (cast) through the 3D scene. So making a line from the camera's view and something to do with the mouse... we represent this raycast. I've added lines 7-10 below:

  this.initEventListeners = () => {
    const onDocumentMouseDown = (event) => {
      // Set mouse params
      event.preventDefault();
      this.mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
      this.mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
      // Check if the mouse click intersects the objects
      this.raycaster.setFromCamera( this.mouse, this.camera );
      const intersects = this.raycaster.intersectObjects( this.scene.children );
      console.log('intersects', intersects);
    }
    document.addEventListener( 'mousedown', onDocumentMouseDown, false )

Logging the intersects, we should see that at the first position of the array is the closest thing we intersect. So if we make an event happen for when our object is the first thing in the array, then we're done!