12

Say I have some elements in the canvas, they may be overridden with each other. When clicking on a point, how can I get that very element?

update: this demo helps a lot: http://mrdoob.github.com/three.js/examples/canvas_interactive_cubes.html

Nami WANG
  • 1,532
  • 4
  • 19
  • 30
  • http://stackoverflow.com/questions/8292486/three-js-how-to-detect-what-shape-was-selected-after-drag – BorisD Dec 08 '11 at 16:31

4 Answers4

21

Use the following code. This will allow you to add a click event and do what you need to when that happens. You can view the source of the page to see what they are doing which is were I got this code from.

document.addEventListener( 'mousedown', onDocumentMouseDown, false );

var projector = new THREE.Projector();

function onDocumentMouseDown( event ) {

    event.preventDefault();

    var vector = new THREE.Vector3(
        ( event.clientX / window.innerWidth ) * 2 - 1,
      - ( event.clientY / window.innerHeight ) * 2 + 1,
        0.5
    );
    projector.unprojectVector( vector, camera );

    var ray = new THREE.Ray( camera.position, 
                             vector.subSelf( camera.position ).normalize() );

    var intersects = ray.intersectObjects( objects );

    if ( intersects.length > 0 ) {

        intersects[ 0 ].object.materials[ 0 ].color.setHex( Math.random() * 0xffffff );

        var particle = new THREE.Particle( particleMaterial );
        particle.position = intersects[ 0 ].point;
        particle.scale.x = particle.scale.y = 8;
        scene.add( particle );

    }

    /*
    // Parse all the faces
    for ( var i in intersects ) {
        intersects[ i ].face.material[ 0 ].color
            .setHex( Math.random() * 0xffffff | 0x80000000 );
    }
    */
}
Philipp Gayret
  • 4,238
  • 1
  • 24
  • 33
Michael Grassman
  • 1,937
  • 12
  • 21
  • 4
    Where is the `projector` variable declared and initialised? – Drew Noakes Sep 19 '12 at 00:54
  • I've added `var projector = new THREE.Projector();` to the answer, didn't do a test run or anything but I'm using practically the same logic in my own code which is taken from the ThreeJS samples. – Philipp Gayret Jan 06 '14 at 19:55
  • `vector.subSelf` does not exist anymore. it should now be `vector.sub`. Also, `unprojectVector` is now `unproject`. – antoineMoPa Feb 16 '17 at 14:56
5

The example you link to has a simple API for this.

Put this in your HTML. You'll have to download the script and make sure it loads.

<script src='threex.domevent.js'></script>

Then, on your mesh object, call the following:

mesh.on('click', function()
{
    // response to click...
    mesh.scale.x *= 2;
});

Or a more interesting example that animates the rotation and color of an object smoothly:

mesh.on('click', function(event)
{
    var object3d = event.target,
        rotation, color;
    if (object3d.rotation.x < Math.PI / 4) {
        rotation = {x: Math.PI / 2};
        color = {r: 1, g: 0.5, b: 0};
    } else {
        rotation = {x: 0};
        color = {r: 0.5, g: 0.75, b: 0.25};
    }
    new TWEEN.Tween(object3d.rotation)
        .to(rotation, 800)
        .easing(TWEEN.Easing.Bounce.EaseOut)
        .start();
    new TWEEN.Tween(object3d.material.color)
        .to(color, 300)
        .easing(TWEEN.Easing.Quartic.EaseIn)
        .start();
})
Drew Noakes
  • 266,361
  • 143
  • 616
  • 705
  • 2
    it says Uncaught TypeError: Object [object Object] has no method 'on' mazeJs.js:198 – Rickie Aug 16 '13 at 19:27
  • @Rickie, what is the type of your `mesh` instance? – Drew Noakes Aug 16 '13 at 23:59
  • This script is reallly cool and I would love to utilize it but I am also getting an error: Uncaught TypeError: mesh.on is not a function.. var img = new THREE.MeshBasicMaterial({ map:THREE.ImageUtils.loadTexture('images/greg.jpg') }); var mesh = new THREE.Mesh(new THREE.PlaneGeometry(50, 50),img); --- I wonder what might be causing the error – Starfs Jun 30 '16 at 21:37
  • @Starfs, what if you try `mesh.addEventListener('click', ...` instead of `mesh.on('click', ...`? – Drew Noakes Jul 02 '16 at 09:02
  • Also be sure to read the documentation of the script I linked to. – Drew Noakes Jul 02 '16 at 09:02
  • Okay cool. Thank you for the response. I went a different route with it but I am going to give this a look soon since it makes the code so much cleaner. – Starfs Jul 05 '16 at 19:21
3

maybe this tool can help you, a full-interaction manager, help three.js easy to binding interaction event

more detial see three.interaction

import { Scene, PerspectiveCamera, WebGLRenderer, Mesh, BoxGeometry, MeshBasicMaterial } from 'three';
import { Interaction } from 'three.interaction';

const renderer = new WebGLRenderer({ canvas: canvasElement });
const scene = new Scene();
const camera = new PerspectiveCamera(60, width / height, 0.1, 100);

// new a interaction, then you can add interaction-event with your free style
const interaction = new Interaction(renderer, scene, camera);

const cube = new Mesh(
  new BoxGeometry(1, 1, 1),
  new MeshBasicMaterial({ color: 0xffffff }),
);
scene.add(cube);
cube.cursor = 'pointer';
cube.on('click', function(ev) {});
cube.on('touchstart', function(ev) {});
cube.on('touchcancel', function(ev) {});
cube.on('touchmove', function(ev) {});
cube.on('touchend', function(ev) {});
cube.on('mousedown', function(ev) {});
cube.on('mouseout', function(ev) {});
cube.on('mouseover', function(ev) {});
cube.on('mousemove', function(ev) {});
cube.on('mouseup', function(ev) {});
// and so on ...

/**
 * you can also listen at parent or any display-tree node,
 * source event will bubble up along with display-tree.
 */
scene.on('touchstart', ev => {
  console.log(ev);
})
scene.on('touchmove', ev => {
  console.log(ev);
})
jasonChen82
  • 151
  • 1
  • 5
1

Drew's answer using THREEx.domevents is outdated. The new API requires initializing a domEvents object and then bind meshes w/ corresponding event listeners to it.

From the Github repo:

var domEvents   = new THREEx.DomEvents(camera, renderer.domElement)
// ...
domEvents.addEventListener(mesh, 'click', function(event){
    console.log('you clicked on the mesh')
}, false)

Also, it's available on bower!

bower install threex.domevents

Edit: Though not documented, there is support for 'touchstart', 'touchend', and a few other mobile events as well. Be sure to look into these if supporting mobile, as mouse 'clicks' aren't always reported as such on some devices.

austin_ce
  • 813
  • 11
  • 27