React Three Fiber实现模型X、Z轴拖拽跟随鼠标的问题
解决Three.js(react-three-fiber)中模型无法跟随鼠标拖拽的问题
你的核心问题在于:直接将鼠标移动量按固定比例叠加到模型位置的方式,没有考虑相机的投影转换(透视/正交),导致模型移动和鼠标位置无法精准对应,自然做不到"完全跟随"的效果。
修正方案:用射线投射(Raycaster)实现精准拖拽
通过Three.js的Raycaster,可以将屏幕上的鼠标坐标转换为3D空间中的位置,让模型严格跟随鼠标在指定平面上移动。以下是修改后的完整代码:
import { useThree, useRaycaster, useMouse } from '@react-three/fiber'; import { useGesture } from '@use-gesture/react'; import { useState, useRef } from 'react'; import * as THREE from 'three'; const BasicOrangeBox = ({ setControlsDisabled, startPosition }) => { const { camera } = useThree(); const raycaster = useRaycaster(); const mouse = useMouse(); const [boxPosition, setBoxPosition] = useState(startPosition); // 保存拖拽开始时,模型与鼠标交点的偏移量,避免模型跳位 const dragOffset = useRef({ x: 0, z: 0 }); const bind = useGesture({ onDragStart: () => { setControlsDisabled(true); // 计算拖拽初始时的射线与模型所在平面的交点 raycaster.setFromCamera(mouse, camera); // 定义拖拽平面:这里固定Y轴位置为模型初始Y值,即模型在XZ平面移动 const dragPlane = new THREE.Plane(new THREE.Vector3(0, 1, 0), -startPosition.y); const intersection = new THREE.Vector3(); raycaster.ray.intersectPlane(dragPlane, intersection); // 记录模型位置与交点的偏移 dragOffset.current.x = boxPosition.x - intersection.x; dragOffset.current.z = boxPosition.z - intersection.z; }, onDrag: () => { raycaster.setFromCamera(mouse, camera); const dragPlane = new THREE.Plane(new THREE.Vector3(0, 1, 0), -startPosition.y); const intersection = new THREE.Vector3(); // 计算当前鼠标对应的3D平面位置 if (raycaster.ray.intersectPlane(dragPlane, intersection)) { setBoxPosition(prev => ({ ...prev, x: intersection.x + dragOffset.current.x, z: intersection.z + dragOffset.current.z })); } }, onDragEnd: () => { setControlsDisabled(false); // 更新初始基准位置 setBoxPosition(prev => ({ ...prev, x0: prev.x, z0: prev.z })); } }); return ( <mesh {...bind()} position={[boxPosition.x, boxPosition.y, boxPosition.z]} > <boxGeometry /> <meshBasicMaterial color={"orange"} /> </mesh> ); };
关键改进点说明
- 射线投射转换:
Raycaster会根据相机参数,将屏幕鼠标坐标映射到3D空间的指定平面上,确保模型移动和鼠标位置完全匹配,不受相机缩放、视角变化影响。 - 拖拽偏移处理:在拖拽开始时记录模型与鼠标交点的偏移,避免模型瞬间跳转到鼠标位置,让拖拽操作更自然。
- 可自定义拖拽平面:如果需要让模型在XY平面(垂直方向)移动,只需修改平面的法向量:
// XY平面拖拽:固定Z轴位置 const dragPlane = new THREE.Plane(new THREE.Vector3(0, 0, 1), -startPosition.z);
原代码的核心问题
原代码中newObj.x = newObj.x0 + (x/100)这种固定比例累加的方式,本质是将屏幕像素移动量直接映射为3D单位,完全忽略了相机的投影关系——当相机拉近/拉远、视角改变时,这个比例会彻底失效,模型移动和鼠标操作的对应关系会完全错乱。
内容的提问来源于stack exchange,提问作者LCAYSS
相关产品推荐
相关产品推荐

