使用CTRL单击选择多个three.js对象?

时间:2018-01-12 05:00:17

标签: javascript three.js logic keyboard-events multiple-select

我正在尝试让用户使用标准的CTRL-click选择多个three.js对象。单选是完美的。在进行CTRL点击时,我得到了我无法理解的行为。我在SELECTED数组中得到重复的条目,当我单击第二个对象时,有时会将其添加到数组中,有时则不会。并非SELECTED数组中的每个项都应用了转换...我甚至无法理解我得到的结果。我认为这个问题在某种程度上与if ( event.type === 'click' && event.ctrlKey )函数中mouseEventHandler()语句中的语句逻辑有关,但我无法弄清楚我做错了什么。

这是我的相关代码:

var ray = new THREE.Raycaster();
var mouse = new THREE.Vector2();
var INTERSECTED;  // Object closest to the camera
var SELECTED = [];    // Object selected via dblclick

function onMouse( event ) {

    event.preventDefault();

    // calculate mouse position in normalized device coordinates
    // (-1 to +1) for both components

    mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
    mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;

    mouseEventHandler( event );

}

function mouseEventHandler( event /* , fn, revFn */ ){

    // update the picking ray with the camera and mouse position
    ray.setFromCamera( mouse, entities.cameras.perspCamera );

    // calculate objects intersecting the picking ray
    var intersects = ray.intersectObjects( scene.children );

    // if there's at least one intersected object...
    if ( intersects && intersects[0] && intersects[0].object ){

        // Check if the event is a mouse move, INTERSECTED exists and we're sitting on the same INTERSECTED object as the last time this function ran...        
        if ( event.type === 'mousemove' ){
            // Check if the current top-level intersected object is the previous INTERSECTED        
            if ( intersects[ 0 ].object != INTERSECTED ){
                // ... if there is a previous INTERSECTED
                if ( INTERSECTED ) {    
                    // restore the previous INTERSECTED to it's previous state.
                    unTransformGraphElementOnMouseOut( INTERSECTED );                                   
                }                       
                // set the currently intersected object to INTERSECTED  
                INTERSECTED = intersects[ 0 ].object;       
                // and transform it accordingly.
                transformGraphElementOnMouseOver( INTERSECTED );                            
                }   
        }       

        if ( event.type === 'click' && event.ctrlKey ){

            // If there's an INTERSECTED object that's a GraphElement 
            var intersectedIsGraphElement = INTERSECTED && INTERSECTED.isGraphElement;
            var selectedIncludesIntersected = SELECTED && SELECTED.includes( INTERSECTED );
            var selectedDoesntIncludeIntersected = SELECTED && !SELECTED.includes( INTERSECTED );

            if ( !INTERSECTED || !INTERSECTED.isGraphElement ){             
                return;
            }

            if ( intersectedIsGraphElement ) { 

                // If SELECTED includes INTERSECTED, leave it alone.
                if ( selectedIncludesIntersected ) { 
                    return; 
                    }

                // If SELECTED doesn't include INTERSECTED, transform it and add it.
                else if ( selectedDoesntIncludeIntersected ) { 
                    SELECTED.push( INTERSECTED );
                    transformGraphElementOnSelect( INTERSECTED );
                    console.log( SELECTED );
                    }
            }
        }

        if ( event.type === 'click' ){  // If CTRL isn't clicked

            // If there's no INTERSECTED or the INTERSECTED isn't a GraphElement
            if ( !INTERSECTED || !INTERSECTED.isGraphElement ){             
                // If there's a SELECTED Array
                if ( SELECTED ){
                    // for all elements in the SELECTED array
                    for ( var s = 0; s < SELECTED.length; s++ ){
                        // restore them to their unselected state.
                        unTransformGraphElementOnUnselect( SELECTED[ s ] );

                    }
                    // and purge the SELECTED array.
                    SELECTED = [];
                }
            }

            // IF there's an INTERSECTED and it's a GraphElement
            if ( INTERSECTED && INTERSECTED.isGraphElement ){

                // If there's a SELECTED array
                if ( SELECTED ){

                    // If that SELECTED array includes the currently INTERSECTED object
                    if ( SELECTED.includes ( INTERSECTED ) ) { 
                        // Negative for loop -- untransform and remove from SELECTED everything but the INTERSECTED object
                        for ( var s = 0; s < SELECTED.length; s++ ){

                            if ( SELECTED[ s ] !== INTERSECTED ){

                                unTransformGraphElementOnUnselect( SELECTED[ s ] );
                                SELECTED.splice( s, 1 ); 

                            }
                        }
                    }

                    else { 

                        for ( var s = 0; s < SELECTED.length; s++ ){ 
                            unTransformGraphElementOnUnselect( SELECTED[ s ] ); 
                            }
                        SELECTED = [];

                    }
                }

                transformGraphElementOnSelect( INTERSECTED );
                SELECTED.push( INTERSECTED );
            }
        }

        // Check if the mouse event is a wheel event (This is temporary, just to see if we can save a file with the change. We're also going to make it so that the change happens at the level of the graphElement itself, and not just the displayObject )
        if ( event.type === 'wheel' ){
            if ( intersects[ 0 ].object.isGraphElement && intersects[ 0 ].object === INTERSECTED ){
                // transform on wheel.
                transformGraphElementOnWheel( INTERSECTED );                            
            }           
        }

    INTERSECTED && console.log( 'INTERSECTED.isGraphElement: ', INTERSECTED.isGraphElement, 'MouseEvent: ', event.type );           
    }
}

function transformGraphElementOnMouseOver( obj ){
    if ( obj.isGraphElement ) { obj.referent.transformOnMouseOver(); }  
}

function unTransformGraphElementOnMouseOut( obj ){
    if ( obj.isGraphElement ) { obj.referent.transformOnMouseOut(); }
}

function transformGraphElementOnSelect( obj ){
    if ( obj.isGraphElement ) { obj.referent.transformOnDblClick(); }   
}

function unTransformGraphElementOnUnselect( obj ){
    if ( obj.isGraphElement ) { obj.referent.unTransformOnDblClickOutside(); }  
}

function transformGraphElementOnWheel( obj ){
    if ( obj.isGraphElement ) { obj.referent.transformOnWheel(); }  
}

function listenFor(){
    document.getElementById('visualizationContainer').addEventListener( 'click', onMouse, false );
    document.getElementById('visualizationContainer').addEventListener( 'mousemove', onMouse, false );
    document.getElementById('visualizationContainer').addEventListener( 'mousedown', onMouse, false );
    document.getElementById('visualizationContainer').addEventListener( 'dblclick', onMouse, false )
    document.getElementById('visualizationContainer').addEventListener( 'wheel', onMouse, false );
    document.getElementById('visualizationContainer').addEventListener( 'contextmenu', onMouse, false );
}

listenFor();

1 个答案:

答案 0 :(得分:0)

@marekful谢谢你。你的提示是解决方案的一部分,并帮助我揭开其余部分。我的问题的第二部分是我在for循环中运行的splice动作在我循环遍历它们时改变了数组索引,从而使for循环短路。最后,重复发生是因为我在完成处理后将INTERSECTED推回到SELECTED数组中,无论SELECTED是否包括INTERSECTED。

我的代码还有另一个问题 - 当我点击天空时,没有找到任何对象,没有注册点击。但我不认为这与我的问题的范围有关,这似乎已得到解决。这是我mouseEventHandler():

的更新代码
function mouseEventHandler( event /* , fn, revFn */ ){

// update the picking ray with the camera and mouse position
ray.setFromCamera( mouse, entities.cameras.perspCamera );

// calculate objects intersecting the picking ray
var intersects = ray.intersectObjects( scene.children );

// if there's at least one intersected object...
if ( intersects && intersects[0] && intersects[0].object ){

    // Check if the event is a mouse move, INTERSECTED exists and we're sitting on the same INTERSECTED object as the last time this function ran...        
    if ( event.type === 'mousemove' ){
        // Check if the current top-level intersected object is the previous INTERSECTED        
        if ( intersects[ 0 ].object != INTERSECTED ){
            // ... if there is a previous INTERSECTED
            if ( INTERSECTED ) {    
                // restore the previous INTERSECTED to it's previous state.
                unTransformGraphElementOnMouseOut( INTERSECTED );                                   
            }                       
            // set the currently intersected object to INTERSECTED  
            INTERSECTED = intersects[ 0 ].object;       
            // and transform it accordingly.
            transformGraphElementOnMouseOver( INTERSECTED );                            
            }   
    }       

    if ( event.type === 'click' ){

        if ( event.ctrlKey ){

            // If there's an INTERSECTED object that's a GraphElement 
            var intersectedIsGraphElement = INTERSECTED && INTERSECTED.isGraphElement;
            var selectedIncludesIntersected = SELECTED && SELECTED.includes( INTERSECTED );
            var selectedDoesntIncludeIntersected = SELECTED && !SELECTED.includes( INTERSECTED );

            if ( !INTERSECTED || !INTERSECTED.isGraphElement ){             
                return;
            }

            if ( intersectedIsGraphElement ) { 

                // If SELECTED includes INTERSECTED, leave it alone.
                if ( selectedIncludesIntersected ) { 
                    return; 
                    }

                // If SELECTED doesn't include INTERSECTED, transform it and add it.
                else if ( selectedDoesntIncludeIntersected ) { 
                    SELECTED.push( INTERSECTED );
                    transformGraphElementOnSelect( INTERSECTED );
                    console.log( SELECTED );
                    }
            }
        }

        else if ( !event.ctrlKey ){  // If CTRL isn't clicked

            // If there's no INTERSECTED or the INTERSECTED isn't a GraphElement
            if ( !INTERSECTED || !INTERSECTED.isGraphElement ){             
                // If there's a SELECTED Array
                if ( SELECTED ){
                    // for all elements in the SELECTED array
                    for ( var s = 0; s < SELECTED.length; s++ ){
                        // restore them to their unselected state.
                        unTransformGraphElementOnUnselect( SELECTED[ s ] );

                    }
                    // and purge the SELECTED array.
                    SELECTED = [];
                }
            }

            // IF there's an INTERSECTED and it's a GraphElement
            if ( INTERSECTED && INTERSECTED.isGraphElement ){

                // If there's a SELECTED array
                if ( SELECTED ){

                    // If that SELECTED array includes the currently INTERSECTED object
                    if ( SELECTED.includes ( INTERSECTED ) ) { 
                        // Negative for loop -- untransform and remove from SELECTED everything but the INTERSECTED object
                        for ( var s = 0; s < SELECTED.length; s++ ){

                            if ( SELECTED[ s ] !== INTERSECTED ){
                                unTransformGraphElementOnUnselect( SELECTED[ s ] );                             
                            }
                        }
                        SELECTED = [ INTERSECTED ];
                        console.log( SELECTED );
                    }

                    else { 

                        for ( var s = 0; s < SELECTED.length; s++ ){ 
                            unTransformGraphElementOnUnselect( SELECTED[ s ] ); 
                            }
                        SELECTED = [];

                        transformGraphElementOnSelect( INTERSECTED );
                        SELECTED.push( INTERSECTED );                           
                    }
                }
            }
        }
    }


    // Check if the mouse event is a wheel event (This is temporary, just to see if we can save a file with the change. We're also going to make it so that the change happens at the level of the graphElement itself, and not just the displayObject )
    if ( event.type === 'wheel' ){
        if ( intersects[ 0 ].object.isGraphElement && intersects[ 0 ].object === INTERSECTED ){
            // transform on wheel.
            transformGraphElementOnWheel( INTERSECTED );                            
        }           
    }

INTERSECTED && console.log( 'INTERSECTED.isGraphElement: ', INTERSECTED.isGraphElement, 'MouseEvent: ', event.type );           
}

}