1. 程式人生 > >three.js中的事件

three.js中的事件

以上一篇入門篇為例來簡單的設定下3d模型當中的互動事件,上一篇我們已經完成了在3d頁面中添加了一個紅色球,下面我們給這個球一個點選事件讓它Y軸位置上升,再設定一個滑鼠移入到球上時讓其變色。

1.其實three.js當中沒有事件可以直接選中物體的,我們需要監聽window物件來完成與3d頁面的互動,通過使用到three.js當中RayCaster物件,用於在三維空間中進行滑鼠拾取,原理是:相機與滑鼠所在的裝置座標之間的連線與哪些物體相交。相交的物體離螢幕越近的越靠前,所以第一個物體就是我們選中的物件。

第一步:給window物件新增點選事件監聽器;

    window.addEventListener('mousedown', mouseDownFuc);

第二步:在事件監聽函式裡拾取到點選的物件集合;

    function mouseDownFuc(){
        let raycaster = new THREE.Raycaster();//建立光線投射物件
        let mouse = new THREE.Vector2();//建立二維平面
        let intersectsObjArr = getSelsectOBj(mouse,raycaster, e);//通過封裝的getSelsectOBj函式獲取滑鼠選中物件集合,e是點選事件物件
    }     
    //獲取事件操作物件
    function getSelsectOBj(mouse,raycaster, e) {
        //將html座標系轉化為webgl座標系,並確定滑鼠點選位置
        mouse.x =  e.clientX / renderer.domElement.clientWidth*2-1;
        mouse.y =  -(e.clientY / renderer.domElement.clientHeight*2)+1;
        raycaster.setFromCamera(mouse,camera);//以camera為z座標,確定所點選物體的3D空間位置
        let intersects = raycaster.intersectObjects(scene.children, true);//確定所點選位置上的物體數量集合
        return intersects;//返回連線經過的物體集合
    }

第三步:判斷intersectsObjArr集合長度不為零,確認我們滑鼠點選的地方有物體,如果有物體那麼intersectsObjArr[0]就是我們選中的物件,通過頁面console.log(intersectsObjArr[0])發現intersectsObjArr[0]包含了相交點的許多資訊,而我們只需要交點的物件,所以我們需要取到intersectsObjArr[0].object物件;然後我們還需要進行一次判斷當前物件就是我們點選的物件,在這裡我們還得回到建立物體時給它的name屬性一個值進行區分物體。最後給它position屬性值的Y軸座標設為50;

        myBall.name = 'redBall';//建立物體時給它name屬性一個名稱
        if(intersectsObjArr.length > 0){
            if(intersectsObjArr[0].object.name == 'redBall'){
                intersectsObjArr[0].object.position.y = 50;
            }
        }

以上就是我們完成了3d頁面物體點選事件,然後就是滑鼠的移入到物體變色,移出時顏色還原,這和2d頁面的移入移出不太一樣,因為我們是給window物件加的監聽器,然後通過RayCaster物件來拾取到物體的,所以我們這裡需要給window物件加滑鼠移動事件來判斷滑鼠是否移動我們的物體上。其步驟同上點選事件一樣。

    window.addEventListener('mousemove', mouseMoveFuc);
    function mouseMoveFuc(){
        let raycaster = new THREE.Raycaster();//建立光線投射物件
        let mouse = new THREE.Vector2();//建立二維平面
        let intersectsObjArr = getSelsectOBj(mouse,raycaster, e);//通過封裝的getSelsectOBj函式獲取滑鼠選中物件集合,e是點選事件物件
        if(intersectsObjArr.length > 0){
            if(intersectsObjArr[0].object.name == 'redBall'){
                intersectsObjArr[0].object.material = new THREE.MeshPhongMaterial( { color: 'orange'});//移到物體上時顏色變成橘色
                document.getElementsByTagName('body')[0].style.cursor = 'pointer';//移到物體上時滑鼠顯示為手

            }
        }else{
                myBall.material =  new THREE.MeshPhongMaterial( { color: 0xff0000});//移出物體時顏色變成原來的紅色
                document.getElementsByTagName('body')[0].style.cursor = 'default';//移出物體時滑鼠顯示為預設
        }
    }

貼上最終程式碼:執行一下,看下效果

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset=utf-8>
        <title>event</title>
        <style>
            body {
                margin: 0;
                padding: 0;
                height: 100vh;
                width: 100vw;
                overflow: hidden;
                background: url('img/bgImg.jpg') no-repeat fixed;
                background-size: 100% 100%;
            }
        </style>
    </head>
    <body>
    <script src="lib/three.min.js"></script>
    <script src="lib/OrbitControls.js"></script>
    <script>
        window.onload = function(){
            let scene,camera,renderer,myBall;
            initThreeScene();
            //物體的事件互動
            window.addEventListener('mousedown', mouseDownFuc);
            window.addEventListener('mousemove', mouseMoveFuc);
            function mouseDownFuc (e) {
                let raycaster = new THREE.Raycaster();//光線投射,用於確定滑鼠點選位置
        let mouse = new THREE.Vector2();//建立二維平面
        let intersects = getSelsectOBj(mouse,raycaster, e);
                if(intersects.length > 0) {
                    console.log(intersects[0])
                    if(intersects[0].object.name == 'myBall') {
                        myBall.position.y = 50;
                   }
                }
            }
            function mouseMoveFuc (e) {
                let raycaster = new THREE.Raycaster();//光線投射,用於確定滑鼠點選位置
        let mouse = new THREE.Vector2();//建立二維平面
        let intersects = getSelsectOBj(mouse,raycaster, e);
                if(intersects.length > 0) {
                   if(intersects[0].object.name == 'myBall') {
                       myBall.material =  new THREE.MeshPhongMaterial( { color: 'orange'});
                       document.getElementsByTagName('body')[0].style.cursor = 'pointer';
                   }
                }else {
                   myBall.material =  new THREE.MeshPhongMaterial( { color: 0xff0000});
                   document.getElementsByTagName('body')[0].style.cursor = 'default';
                }
            }
            //獲取事件操作物件
            function getSelsectOBj(mouse,raycaster, e) {
                //將html座標系轉化為webgl座標系,並確定滑鼠點選位置
                mouse.x =  e.clientX / renderer.domElement.clientWidth*2-1;
                mouse.y =  -(e.clientY / renderer.domElement.clientHeight*2)+1;
                //以camera為z座標,確定所點選物體的3D空間位置
                raycaster.setFromCamera(mouse,camera);
                //確定所點選位置上的物體數量
                let intersects = raycaster.intersectObjects(scene.children, true);
                return intersects;
            }
            function initThreeScene() {
                scene = new THREE.Scene();
                camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.1, 2000 );
                camera.position.set( 0, 50,300 );   
                renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });//antialias: true;讓渲染的平面是光滑的,alpha: true;讓渲染的3d背景透明。
                renderer.setSize( window.innerWidth, window.innerHeight );
                document.body.appendChild(renderer.domElement );
                // 給場景新增一個環境光
                let ambientLight = new THREE.AmbientLight( 0xf5f5f5);
                scene.add( ambientLight );
                //輔助線
                let grid = new THREE.GridHelper( 400, 30, 0xcccccc, 0xcccccc );
                scene.add( grid );
                //建立的球
                let ball = new THREE.SphereGeometry( 25, 100, 100 );//25:球半徑 第一個100:水平分割面的數量. 第二個100:垂直分割面的數量.
                let ballColor = new THREE.MeshPhongMaterial( { color: 0xff0000 } );
                myBall = new THREE.Mesh( ball , ballColor );
                myBall.name = 'myBall';
                scene.add( myBall );
                let controls =new THREE.OrbitControls(camera, renderer.domElement);
                controls.enableZoom =true;//允許縮放
                //設定相機移動距離
                controls.minDistance = 1;
                controls.maxDistance = 2000;
                controls.enableRotate =true;
                function render() { 
                    requestAnimationFrame( render );
                    renderer.render( scene, camera );
                } 
                render();
                window.onresize = function () {
                    camera.aspect = window.innerWidth / window.innerHeight;//相機重置可視範圍
                    camera.updateProjectionMatrix();
                    renderer.setSize( window.innerWidth, window.innerHeight );//渲染器重新渲染可視範圍
                }
            }
        }
    </script>
    </body>
    </html>