非控制器版
拖动场景中的物体实际上是在一个平行于窗口的平面中进行拖动,确定这个平面并确定鼠标在该平面中的位置变化,就将问题转换成简单的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();//GUIinitRenderer();//初始化渲染器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,zcamera.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
<!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();//GUIinitRenderer();//初始化渲染器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,zcamera.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>