I am trying to make models draggable in three.js. I want my model to follow my mouse when I move it. This is what I am trying to accomplish. I am using react-three-fiber and @use-gesture/react
What I am trying to accomplish
Here is how my program looks
The difference is quite noticeable. On the good example, the model follows the mouse wherever it goes. On my program, that is not the case.
Here is my code for the cube
const BasicOrangeBox = ({setControlsDisabled, startPosition} : BasicOrangeBoxType) => {
const { camera } = useThree();
const [boxPosition, setBoxPosition] = useState(startPosition)
const bind = useGesture({
onDrag: ({movement: [x, y]}) => {
setControlsDisabled(true);
setBoxPosition( (prev) => {
const newObj = {...prev};
newObj.x = newObj.x0 + (x/100);
newObj.z = newObj.z0 + (y/100);
return newObj;
} )
},
onDragEnd: () => {
setControlsDisabled(false);
setBoxPosition( (prev) => {
const newObj = {...prev};
newObj.x0 = newObj.x;
newObj.z0 = newObj.z;
return newObj;
} )
}
})
return (
<mesh
{...bind() }
position={[boxPosition.x, boxPosition.y, boxPosition.z]}
>
<boxGeometry />
<meshBasicMaterial color={"orange"} />
</mesh>
)
}
Here is how I made a mesh cube draggable only on x and z axis like in a video.
Needed packages:
First, I created a plane that spanned across my whole viewport and assigned it to a useRef
<mesh rotation={[MathUtils.degToRad(90), 0, 0]} ref={planeRef} position={[0, -0.01, 0]}>
<planeGeometry args={[innerWidth, innerHeight]} />
<meshBasicMaterial color={0xfffffff} side={DoubleSide} />
</mesh>
Then I added that ref to a useContext so I can use it in different components.
Next, I imported raycaster from useThree hook and planeRef from aforementioned useContext.
Then I used useGesture onDrag and onDragEnd to enable and disable my OrbitControls
Inside the onDrag, I used raycaster's intersectsObject method and added an array of only one element, my plane, as a parameter. This gave me x, y, z coordinates where my mouse intersects with the plane. (Y is always 0)
Then I updated my box position.
Here is the full code snippet
const BasicOrangeBox = ({setControlsDisabled, startPosition} : BasicRedBoxType) => {
const { raycaster } = useThree();
const [boxPosition, setBoxPosition] = useState(startPosition);
const planeRef = useContext(PlaneContext);
const bind = useGesture({
onDrag: () => {
const intersects = raycaster.intersectObjects([planeRef]);
if (intersects.length > 0){
const intersection = intersects[0];
console.log(intersection.point.x);
setBoxPosition({
x: intersection.point.x,
y: intersection.point.y,
z: intersection.point.z,
})
}
setControlsDisabled(true);
},
onDragEnd: () => {
setControlsDisabled(false);
}
})
return ( //@ts-ignore Ignores type error on next line
<mesh
{...bind() }
position={[boxPosition.x, boxPosition.y, boxPosition.z]}
>
<boxGeometry />
<meshBasicMaterial color={"orange"} />
</mesh>
)
}