Three.js - 使用 THREE.DragControls 实现模型拖拽

需求:鼠标略过模型出现三维坐标轴,通过点击三维坐标轴,在 x、y、z 轴上移动,并且通过点中模型任意拖拽模型。
本质上,使用鼠标拖动三维模型,就是把鼠标的拖动距离,转化为三维模型的位置或角度变化量,使用three、js的拖拽控件·DragControls.js·就可以很容容易实现。
示例浏览地址:
https://ithanmang.gitee.io/threejs/home/201807/20180703/03-raycaster-dragControls.html

引入库文件

<!--拖拽控件-->
<script src="../../libs/examples/js/controls/DragControls.js"></script>
<!--可视化平移控件-->
<script src="../../libs/examples/js/controls/TransformControls.js"></script>

创建对象

// 添加拖拽控件
function initDragControls() {
    // 添加平移控件
    var transformControls = new THREE.TransformControls(camera, renderer.domElement);
    scene.add(transformControls);

    // 过滤不是 Mesh 的物体,例如辅助网格对象
    var objects = [];
    for (let i = 0; i < scene.children.length; i++) {
        if (scene.children[i].isMesh) {
            objects.push(scene.children[i]);
        }
    }
    // 初始化拖拽控件
    var dragControls = new THREE.DragControls(objects, camera, renderer.domElement);

    // 鼠标略过事件
    dragControls.addEventListener('hoveron', function (event) {
        // 让变换控件对象和选中的对象绑定
        transformControls.attach(event.object);
    });
    // 开始拖拽
    dragControls.addEventListener('dragstart', function (event) {
        controls.enabled = false;
    });
    // 拖拽结束
    dragControls.addEventListener('dragend', function (event) {
        controls.enabled = true;
    });
}

效果
Three.js - 使用 THREE.DragControls 实现模型拖拽_第1张图片

注意:拖拽的时候回影响其他的事件,最好处理一下

TransformControls方法

transformControls 配合拖拽控件就可以实现可视化操作三维模型。
通过拖拽控件选中一个三维模型,然后通过拖拽控件对象的方法attach()可以把选中的三维模型与控件transformControls绑定, 可以把控件对象transformControls理解为一个特殊的三维模型。
例如平移状态,控件对象TransformControls就是一个三维坐标轴,通过拖动坐标轴就可以移动与坐标轴绑定的三维模型。

DragControls方法

查看源码可以得知,DragControls具有选中模型的作用。
Three.js - 使用 THREE.DragControls 实现模型拖拽_第2张图片
构造函数
THREE.DragControls = function ( _objects, _camera, _domElement )
参数
_objects:对象数组。
_camera:相机
_domElement:渲染器

可以添加的事件
dragControls.addEventListener()

hoveron – 鼠标划中
hoveroff – 鼠标划过
dragstart – 开始拖拽
dragend – 拖拽结束

示例代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>拖拽控件</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }

        #label {
            position: absolute;
            padding: 10px;
            background: rgba(255, 255, 255, 0.6);
            line-height: 1;
            border-radius: 5px;
        }
    </style>
    <script src="../../libs/build/three-r93.js"></script>
    <script src="../../libs/jquery-1.9.1.js"></script>
    <script src="../../libs/examples/js/Detector.js"></script>
    <script src="../../libs/examples/js/controls/TrackballControls.js"></script>
    <script src="../../libs/examples/js/libs/dat.gui.min.js"></script>
    <script src="../../libs/examples/js/libs/stats.min.js"></script>

    <!--拖拽控件-->
    <script src="../../libs/examples/js/controls/DragControls.js"></script>
    <!--可视化平移控件-->
    <script src="../../libs/examples/js/controls/TransformControls.js"></script>
</head>
<body>
<div id="WebGL-output"></div>
<div id="Stats-output"></div>

<div id="label"></div>
<script>

    var stats = initStats();
    var scene, camera, renderer, controls, light, selectObject;

    // 场景
    function initScene() {
        scene = new THREE.Scene();
    }

    // 相机
    function initCamera() {
        camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 10000);
        camera.position.set(0, 400, 600);
        camera.lookAt(new THREE.Vector3(0, 0, 0));
    }

    // 渲染器
    function initRenderer() {
        if (Detector.webgl) {
            renderer = new THREE.WebGLRenderer({antialias: true});
        } else {
            renderer = new THREE.CanvasRenderer();
        }
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.setClearColor(0x050505);
        document.body.appendChild(renderer.domElement);
    }

    // 初始化模型
    function initContent() {
        var helper = new THREE.GridHelper(1200, 50, 0xCD3700, 0x4A4A4A);
        scene.add(helper);

        var cubeGeometry = new THREE.BoxGeometry(100, 100, 100);
        var cubeMaterial = new THREE.MeshLambertMaterial({color: 0x9370DB});
        var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
        cube.position.y = 50;
        cube.name = "cube";
        scene.add(cube);

        var sphereGeometry = new THREE.SphereGeometry(50, 50, 50, 50);
        var sphereMaterial = new THREE.MeshLambertMaterial({color: 0x3CB371});
        var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
        sphere.position.x = 200;
        sphere.position.y = 50;
        sphere.name = "sphere";
        // sphere.position.z = 200;
        scene.add(sphere);

        var cylinderGeometry = new THREE.CylinderGeometry(50, 50, 100, 100);
        var cylinderMaterial = new THREE.MeshLambertMaterial({color: 0xCD7054});
        var cylinder = new THREE.Mesh(cylinderGeometry, cylinderMaterial);
        cylinder.position.x = -200;
        cylinder.position.y = 50;
        cylinder.name = "cylinder";
        scene.add(cylinder);
    }

    // 鼠标双击触发的方法
    function onMouseDblclick(event) {

        // 获取 raycaster 和所有模型相交的数组,其中的元素按照距离排序,越近的越靠前
        var intersects = getIntersects(event);

        // 获取选中最近的 Mesh 对象
        if (intersects.length != 0 && intersects[0].object instanceof THREE.Mesh) {
            selectObject = intersects[0].object;
            changeMaterial(selectObject);
        } else {
            alert("未选中 Mesh!");
        }
    }

    // 获取与射线相交的对象数组
    function getIntersects(event) {
        event.preventDefault();
        console.log("event.clientX:" + event.clientX)
        console.log("event.clientY:" + event.clientY)

        // 声明 raycaster 和 mouse 变量
        var raycaster = new THREE.Raycaster();
        var mouse = new THREE.Vector2();

        // 通过鼠标点击位置,计算出 raycaster 所需点的位置,以屏幕为中心点,范围 -1 到 1
        mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
        mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

        //通过鼠标点击的位置(二维坐标)和当前相机的矩阵计算出射线位置
        raycaster.setFromCamera(mouse, camera);

        // 获取与射线相交的对象数组,其中的元素按照距离排序,越近的越靠前
        var intersects = raycaster.intersectObjects(scene.children);

        //返回选中的对象
        return intersects;
    }

    // 窗口变动触发的方法
    function onWindowResize() {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
    }

    // 键盘按下触发的方法
    function onKeyDown(event) {
        switch (event.keyCode) {
            case 13:
                initCamera();
                initControls();
                break;
        }
    }

    // 改变对象材质属性
    function changeMaterial(object) {

        var material = new THREE.MeshLambertMaterial({
            color: 0xffffff * Math.random(),
            transparent: object.material.transparent ? false : true,
            opacity: 0.8
        });
        object.material = material;
    }

    // 初始化轨迹球控件
    function initControls() {
        controls = new THREE.TrackballControls(camera, renderer.domElement);
        // controls.noRotate = true;
        controls.noPan = true;
        // 视角最小距离
        controls.minDistance = 1000;
        // 视角最远距离
        controls.maxDistance = 5000;
    }

    // 添加拖拽控件
    function initDragControls() {
        // 添加平移控件
        var transformControls = new THREE.TransformControls(camera, renderer.domElement);
        scene.add(transformControls);

        // 过滤不是 Mesh 的物体,例如辅助网格
        var objects = [];
        for (let i = 0; i < scene.children.length; i++) {
            if (scene.children[i].isMesh) {
                objects.push(scene.children[i]);
            }
        }
        // 初始化拖拽控件
        var dragControls = new THREE.DragControls(objects, camera, renderer.domElement);

        // 鼠标略过事件
        dragControls.addEventListener('hoveron', function (event) {
            // 让变换控件对象和选中的对象绑定
            transformControls.attach(event.object);
        });
        // 开始拖拽
        dragControls.addEventListener('dragstart', function (event) {
            controls.enabled = false;
        });
        // 拖拽结束
        dragControls.addEventListener('dragend', function (event) {
            controls.enabled = true;
        });
    }

    // 初始化灯光
    function initLight() {
        light = new THREE.SpotLight(0xffffff);
        light.position.set(-300, 600, -400);
        light.castShadow = true;

        scene.add(light);
        scene.add(new THREE.AmbientLight(0x5C5C5C));
    }

    // 初始化 dat.GUI
    function initGui() {
        // 保存需要修改相关数据的对象
        gui = new function () {

        }
        // 属性添加到控件
        var guiControls = new dat.GUI();
    }

    // 初始化性能插件
    function initStats() {
        var stats = new Stats();

        stats.domElement.style.position = 'absolute';
        stats.domElement.style.left = '0px';
        stats.domElement.style.top = '0px';

        document.body.appendChild(stats.domElement);
        return stats;
    }

    // 更新div的位置
    function renderDiv(object) {
        // 获取窗口的一半高度和宽度
        let halfWidth = window.innerWidth / 2;
        let halfHeight = window.innerHeight / 2;

        // 逆转相机求出二维坐标
        let vector = object.position.clone().project(camera);

        // 修改 div 的位置
        $("#label").css({
            left: vector.x * halfWidth + halfWidth,
            top: -vector.y * halfHeight + halfHeight - object.position.y
        });
        // 显示模型信息
        $("#label").text("name:" + object.name);
    }

    // 更新控件
    function update() {
        stats.update();
        controls.update();
        controls.handleResize();
        transformControls.update();
    }

    // 初始化
    function init() {
        initScene();
        initCamera();
        initRenderer();
        initContent();
        initLight();
        initControls();
        initGui();
        initDragControls();
        // addEventListener('dblclick', onMouseDblclick, false);
        addEventListener('resize', onWindowResize, false);
        addEventListener('keydown', onKeyDown, false);
    }

    function animate() {
        if (selectObject != undefined && selectObject != null) {
            renderDiv(selectObject);
        }
        requestAnimationFrame(animate);
        renderer.render(scene, camera);
        update();
    }

    init();
    animate();

</script>
</body>
</html>

你可能感兴趣的:(threejs)