three.js 拖动场景中物体(原生|拖拽控制器)

非控制器版

three.js 拖动场景中物体(原生|拖拽控制器)_第1张图片
拖动场景中的物体实际上是在一个平行于窗口的平面中进行拖动,确定这个平面并确定鼠标在该平面中的位置变化,就将问题转换成简单的2d移动物体了

<!DOCTYPE html>
<html>
<head>
    <meta charset=utf-8>
    <title>015-拖动物体</title>
    <link rel="stylesheet" href="./common.css">
</head>
<body onload="init()">
<script src="../lib/three94.js"></script>
<script src="../lib/stats.min.js"></script>
<script src="../lib/dat.gui.min.js"></script>
<script src="../lib/controls/OrbitControls.js"></script>
<script>
  /**
   * 3D场景中进行平移,在不旋转的情况下,只看到物体跟随鼠标以平行于窗口的轨迹运动
   */
  let renderer, camera, scene,
    light, mesh,
    cameraControls;

  function initGui() {
    //声明一个保存需求修改的相关数据的对象
    let gui = {};
    let datGui = new dat.GUI();
    //将设置属性添加到gui当中,gui.add
  }

  function init() {
    initGui();//GUI
    initRenderer();//初始化渲染器
    initScene();//初始化场景
    initLight();//初始化光
    initCamera();//初始化相机
    initMesh();//初始化模型
    addEvent();
    initControls();//轨道相机控制器
    animate();//帧动画
    window.onresize = onWindowResize;
  }

  let ac,//当前活动对象
    plane,//平移平面由活动对象初始位置和当前相机方向向量确定
    startPosition,//目标位置,使用终末位置计算平移量,当然也可以使用递增量
    startMouseWorldPosition,//拖动起始点射线与平移平面的交点
    endMouseWorldPosition;//拖动结束点射线与平移平面的交点

  function addEvent() {
    renderer.domElement.addEventListener('mousedown', function (e) {
      //鼠标落点处创建射线
      let raycaster = screenToWorld(e.clientX, e.clientY);
      //获取射线经过的在指定范围内的物体集合
      let intersect = raycaster.intersectObjects(scene.getObjectByName('moveable').children);
      if (intersect.length > 0) {
        ac = intersect[0].object;
        //创建经过物体中心点的垂直于相机方向的平面
        plane = new THREE.Plane();
        plane.setFromNormalAndCoplanarPoint(camera.getWorldDirection(plane.normal), ac.position);
        //如果使用世界原点构建平面会导致物体位移和鼠标位移不对等,应该使用物体的初始位置中心作为视角切面平面
        // plane.setFromNormalAndCoplanarPoint(camera.getWorldDirection(plane.normal), new THREE.Vector3());
        startMouseWorldPosition = new THREE.Vector3();
        raycaster.ray.intersectPlane(plane, startMouseWorldPosition);
        //备份物体初始点
        startPosition = ac.position.clone();
      }
    });
    renderer.domElement.addEventListener('mousemove', function (e) {
      if (ac) {
        e.preventDefault();
        e.stopPropagation();
        //鼠标移动点处创建射线
        let raycaster = screenToWorld(e.clientX, e.clientY);
        endMouseWorldPosition = new THREE.Vector3();
        //目标点射线与平移平面的焦点即为平移目标点
        raycaster.ray.intersectPlane(plane, endMouseWorldPosition);
        //计算平移向量
        let addVector3 = endMouseWorldPosition.sub(startMouseWorldPosition);
        let target = startPosition.clone().add(addVector3).clone();
        ac.position.copy(target)
      }
    });
    renderer.domElement.addEventListener('mouseup', function (e) {
      ac = false
    })
  }


  function initRenderer() {
    renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);
  }

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

  function initCamera() {
    camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 200);
    camera.position.set(0, 0, 35);//x,y,z
    camera.lookAt(0, 0, 0);
    scene.add(camera);
  }

  function initLight() {
    scene.add(new THREE.AmbientLight(0x444444));

    light = new THREE.DirectionalLight(0xffffff);
    light.position.set(60, 30, 0);

    //平行光开启阴影投射
    light.castShadow = true;

    scene.add(light);
  }

  function initMesh() {
    let group = new THREE.Group();
    group.name = 'moveable';
    material = new THREE.MeshLambertMaterial({color: 'red'});
    geometry = new THREE.BoxGeometry(20, 2, 2);
    mesh = new THREE.Mesh(geometry, material);
    group.add(mesh);
    scene.add(group);


    let helper = new THREE.AxesHelper(50);
    scene.add(helper);
  }

  function initControls() {
    cameraControls = new THREE.OrbitControls(camera, renderer.domElement);
  }

  function render() {
    renderer.render(scene, camera);
  }

  function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
  }

  /**
   *
   * @param offsetX 鼠标相对于画布左上角横向坐标
   * @param offsetY 鼠标相对于画布左上角纵向坐标
   * @returns {Raycaster|Raycaster}
   */
  function screenToWorld(offsetX, offsetY) {
    let x = (offsetX / window.innerWidth) * 2 - 1,
      y = -(offsetY / window.innerHeight) * 2 + 1;
    let raycaster = new THREE.Raycaster();
    raycaster.setFromCamera(new THREE.Vector2(x, y), camera);
    return raycaster;
  }

  function animate() {
    requestAnimationFrame(animate);
    cameraControls.update();
    render()
  }

</script>
</body>

控制器版

DragControls
three.js 拖动场景中物体(原生|拖拽控制器)_第2张图片

<!DOCTYPE html>
<html>
<head>
    <meta charset=utf-8>
    <title>001-拖放控制器</title>
    <link rel="stylesheet" href="../common.css">
</head>
<body onload="init()">
<script src="../../lib/three94.js"></script>
<script src="../../lib/stats.min.js"></script>
<script src="../../lib/dat.gui.min.js"></script>
<script src="../../lib/controls/DragControls.js"></script>
<script src="../../lib/controls/OrbitControls.js"></script>
<script>
  /**
   * 3D场景中进行平移,在不旋转的情况下,只看到物体跟随鼠标以平行于窗口的轨迹运动
   */
  let renderer, camera, scene,
    light, mesh,
    cameraControls;

  function initGui() {
    //声明一个保存需求修改的相关数据的对象
    let gui = {};
    let datGui = new dat.GUI();
    //将设置属性添加到gui当中,gui.add
  }

  function init() {
    initGui();//GUI
    initRenderer();//初始化渲染器
    initScene();//初始化场景
    initLight();//初始化光
    initCamera();//初始化相机
    initMesh();//初始化模型
    initControls();//轨道相机控制器
    animate();//帧动画
    window.onresize = onWindowResize;
  }

  function initRenderer() {
    renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);
  }

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

  function initCamera() {
    camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 200);
    camera.position.set(0, 0, 35);//x,y,z
    camera.lookAt(0, 0, 0);
    scene.add(camera);
  }

  function initLight() {
    scene.add(new THREE.AmbientLight(0x444444));

    light = new THREE.DirectionalLight(0xffffff);
    light.position.set(60, 30, 0);

    //平行光开启阴影投射
    light.castShadow = true;

    scene.add(light);
  }

  function initMesh() {
    let group = new THREE.Group();
    group.name = 'moveable';
    material = new THREE.MeshLambertMaterial({color: 'red'});
    geometry = new THREE.BoxGeometry(20, 2, 2);
    mesh = new THREE.Mesh(geometry, material);
    group.add(mesh);
    scene.add(group);


    let helper = new THREE.AxesHelper(50);
    scene.add(helper);
  }

  function initControls() {
    let dragControls = new THREE.DragControls(scene.getObjectByName('moveable').children, camera, renderer.domElement);

    dragControls.addEventListener('dragstart', function (event) {
      cameraControls.enabled =false;
      event.object.material.emissive.set(0xaaaaaa);
    });

    dragControls.addEventListener('dragend', function (event) {
      event.object.material.emissive.set(0x000000);
      cameraControls.enabled =true;
    });
    cameraControls = new THREE.OrbitControls(camera, renderer.domElement);
  }

  function render() {
    renderer.render(scene, camera);
  }

  function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
  }


  function animate() {
    requestAnimationFrame(animate);
    cameraControls.update();
    render()
  }

</script>
</body>

你可能感兴趣的:(学习,three.js,3d)