【问题标题】:Use CTRL-click to select multiple three.js objects?使用 CTRL 单击选择多个 three.js 对象?
【发布时间】:2018-01-12 05:00:16
【问题描述】:

我正在尝试使用户能够使用标准的 CTRL 键单击来选择多个 three.js 对象。单选工作完美。进行 CTRL-click 时,我得到了我无法理解的行为。我的 SELECTED 数组中有重复的条目,当我单击第二个对象时,有时它会添加到数组中,有时不会。并非 SELECTED 数组中的每个项目都应用了转换......我什至无法真正理解我得到的结果。我认为问题出在mouseEventHandler() 函数中if ( event.type === 'click' && event.ctrlKey ) 语句中我的语句的逻辑上,但我就是不知道我做错了什么。

这是我的相关代码:

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();

【问题讨论】:

  • event.type === 'click' &amp;&amp; event.ctrlKeyevent.type === 'click' 总是在 ctrl+click 的情况下执行。他们不应该在 if else 中吗?
  • @marekful 谢谢。这不是整个解决方案,而是其中的一部分。我在下面的答案中有详细信息。

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


【解决方案1】:

@marekful 谢谢。您的提示是解决方案的一部分,并帮助我解开了其余部分。我的问题的第二部分是我在 for 循环中运行的拼接操作在我循环它们时改变了数组索引本身,从而使 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 );           
}

}

【讨论】:

  • 另一种确定方法是创建一系列函数来处理您的对象数组。即,编写一个 addElement() 函数,该函数接受一个新对象并在它不在数组中时附加它,一个 purge() 函数将其擦除干净等。如果你想构建多个具有相同功能的集合,它可能会派上用场.
猜你喜欢
  • 2013-07-31
  • 1970-01-01
  • 2013-01-04
  • 1970-01-01
  • 2012-08-01
  • 2014-07-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多