removeEventListener isn't working inside my if statement in three.js

Viewed 194

I am a new to JavaScript (but not new to mathematics(coordinate systems, trigonometry, and some of the other stuff involved in three.js)). I have been doing code academy for JavaScript for two weeks, and I tried to follow a tutorial on YouTube to make shapes in three.js and for things to happen when you click your mouse or when you hover over an object. Even though I was following a tutorial I had to watch like 5 different ones on the same subject to be able to make it work but I still have a problem.

I have 5 objects, that if you hover the mouse over them, they slightly rotate (like by 40 degrees, but that's irrelevant), and when you move the mouse off the object, it slightly rotates in reverse back to the starting position. If I click on any of them, I have a tween for the camera that moves closer towards the object. If I click on that same object again, it will tween the camera back to where it was when the scene started.

I created event listeners for 'mousemove' and 'onclick' and they work....but what I want is that if a person has clicked on one of the objects and the camera moves towards it, I want to remove the hover effect (then to have things go back to normal if they click on the object again which will tween the camera back to the origin).

Currently I have an if statement with the raycaster thing for 3D objects, so I TRIED to add a line inside the block of code that executes when I click on an object because that is when I want the eventlistener to not be active. I tried doing google searches, I looked through stackoverflow, I watched more YouTube videos, I checked the MDN website and I see that I am following the instructions exactly (as far as i can tell) and it just isn't working. Could it be something that i wasn't even taking into account like eventlisteners can't be removed inside if statements or something.

If that is the case, then is there a way I can disable the tween that handles the slight rotation while in the particular case of the if statement only?

I tried to do this by adding a line to the case in my if statement that executes when the tween triggered by the click event but nothing happens.

//ONE OF THE OBJECTS FOR REFERENCE
const minigeometry1 = new THREE.CylinderGeometry(5, 5, 0.5, 50);
const minimaterial1 = new THREE.MeshLambertMaterial();
const minicylinder1 = new THREE.Mesh(minigeometry1, minimaterial1);
minicylinder1.name = 'minicylinder1';
minicylinder1.position.z = 7.5;
minicylinder1.position.x = -40;
minicylinder1.rotation.z = Math.PI / 2;
minicylinder1.rotation.y = Math.PI / 2;
scene.add(minicylinder1);

//TWEEN ANIMATIONS FOR CLICK EVENTS
function tweenCamera(finalPosition, tweenSpeed) {
    let initialPosition = new THREE.Vector3(camera.position.x, camera.position.y, camera.position.z);
    new TWEEN.Tween(initialPosition)
        .to(finalPosition, tweenSpeed)
        .onUpdate(() => {
            camera.position.set(initialPosition.x, initialPosition.y, initialPosition.z);
        })
        .easing(TWEEN.Easing.Cubic.Out)
        .start();
}
//WHERE THE CAMERA IS CURRENTLY LOOKING, I THINK
let currentTarget = new THREE.Vector3();
currentTarget.set(controls.target.x, controls.target.y, controls.target.z);

let originTarget =  new THREE.Vector3();
originTarget.set(0,0,0);

//TELLS CAMERA TO ORIENT TOWARD SPECIFIED OBJECT (target controls = camera orientation?)
let cylinderTarget = new THREE.Vector3();
cylinderTarget.set(cylinder.position.x, cylinder.position.y - 15, cylinder.position.z);
let minicylinder1Target = new THREE.Vector3();
minicylinder1Target.set(minicylinder1.position.x, minicylinder1.position.y, minicylinder1.position.z);

//THIS DETERMINES WHETHER WE ARE ZOOMING IN TO, OR OUT OF, OUR SELECTION
let coin1Clicked = false;
let coin2Clicked = false;
let coin3Clicked = false;
let coin4Clicked = false;

Here are my event listeners.....

//THESE ARE AT THE BOTTOM OF MY CODE BUT THEY RUN WITHOUT ISSUE
window.addEventListener("click", (event) => {
    onClick(event);
})
window.addEventListener("mousemove", (event) => {
    onMouseMove(event);
})

Here is my onClick function and the onMouseMove function below that one. You can see here that in the first case of my if statement about I tried using the removeEventListener(17 lines down from here) which didn't work.

function onClick(event) {
    //Normalized mouse coordinates (-1, +1)
    mouse.x = (event.offsetX / window.innerWidth) * 2 - 1;
    mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

    raycaster.setFromCamera(mouse, camera);

    const intersects = raycaster.intersectObjects(scene.children, true);

    if ((getIndexInIntersectsArray(cylinder, intersects) > -1)) {
        window.open(cylinder.userData.URL);
    }

    //IF WE CLICK ON COIN1 FROM THE MAIN POSITION, WE WILL ZOOM INTO IT
    if ((getIndexInIntersectsArray(minicylinder1, intersects) > -1) && (!coin1Clicked)) {
        coin1Clicked = true;
        window.removeEventListener("mousemove", (event) => {
            onMouseMove(event);
        })
        //CODE THAT ACHIEVES MY SLIGHT ROTATION THAT I FOUND ON STACKOVERFLOW
        var start = {}
        start.y = 0;
        var targ = {};
        targ.y = 0;

        function rot(s,t) {
            start["y"] = s;
            targ["y"] = t;
        }  
        rot(1.5*Math.PI/2, 4.5*Math.PI);
        const spun1 = new TWEEN.Tween(start).to(targ, 1500).onUpdate(function() {minicylinder1.rotation.y = start.y}).easing(TWEEN.Easing.Quintic.Out);
        spun1.start();
        new TWEEN.Tween(currentTarget)
            .to(minicylinder1Target, 2500)
            .onUpdate(function() {
                controls.target = currentTarget; 
            })
            .easing(TWEEN.Easing.Quartic.InOut)
            .start()
        //RESTRICT CAMERA ROTATION AND ZOOM OUT
        tweenCamera(new THREE.Vector3(minicylinder1.position.x, minicylinder1.position.y, 25), 2500)
        return coin1Clicked;
    } else if ((getIndexInIntersectsArray(minicylinder1, intersects) > -1) && (coin1Clicked)) {
        //TWEEN TO ORIENT CAMERA TOWARDS ORIGIN
        new TWEEN.Tween(currentTarget)
            .to(cylinderTarget, 2500)
            .onUpdate(function() {
                controls.target = currentTarget; 
            })
            .easing(TWEEN.Easing.Quartic.InOut)
            .start()
        coin1Clicked = false;
        //MOVES CAMERA BACK TO THE ORIGIN
        tweenCamera(new THREE.Vector3(minicylinder3.position.x, 20, 60), 2500)
        return coin1Clicked;
    } else if ((getIndex....................................

SAME FOR OBJECTS 2 THROUGH 4


let coin1Hover = false;

function onMouseMove(event) {
    mouse.x = (event.offsetX / window.innerWidth) * 2 - 1;
    mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

    raycaster.setFromCamera(mouse, camera);

    const intersects = raycaster.intersectObjects(scene.children, true);
    
    if ((getIndexInIntersectsArray(minicylinder1, intersects) > -1) && !coin1Hover) {
        coin1Hover = true;
        var start = {}
        start.y = 0;
        var targ = {};
        targ.y = 0;

        function rot(s,t) {
            start["y"] = s;
            targ["y"] = t;
        }  
        rot(Math.PI/2, 1.5*Math.PI/2);
        const spinning1 = new TWEEN.Tween(start).to(targ, 350).onUpdate(function() {minicylinder1.rotation.y = start.y});
        spinning1.start();
        return coin1Hover
    } else if ((getIndexInIntersectsArray(minicylinder1, intersects) > -1) && coin1Hover) {
        
    } else if ((getIndexInIntersectsArray(minicylinder1, intersects) == -1) && coin1Hover) {
        coin1Hover = false;
        var start = {}
        start.y = 0;
        var targ = {};
        targ.y = 0;

        function rot(s,t) {
            start["y"] = s;
            targ["y"] = t;
        }  
        rot(1.5*Math.PI/2, Math.PI/2);
        const backwards = new TWEEN.Tween(start).to(targ, 350).onUpdate(function() {minicylinder1.rotation.y = start.y});
        backwards.start();
        return coin1Hover
    }
    
    function getIndexInIntersectsArray(elem, array) {
        var ind = -1;
        for (var i = 0; i < array.length; i++) {
            if (array[i].object.name == elem.name) {
                ind = i;
            }
        }
        return ind;
    }
}

Please, if anyone can help I would truly appreciate it. I don't want to sound dramatic but this is something my dad won't stop harassing me over and I can't take the feeling of failure yet again. :-(

1 Answers

Your are adding event listeners with this pattern right now:

window.addEventListener("mousemove", (event) => {
    onMouseMove(event);
});

And remove them like so:

window.removeEventListener("mousemove", (event) => {
    onMouseMove(event);
});

You have to be aware that you assign two different anonymous functions to both method calls. In this way, it's impossible to remove the correct event listener. Use this pattern instead:

window.addEventListener("mousemove", onMouseMove);

And later:

window.removeEventListener("mousemove", onMouseMove);
Related