Moving mesh in Canvas

I am new in Canvas and Three.js. I am looking for the right way to move a mesh inside a Canvas. I created a sandbox but I don’t know if it is the right way. Basically, I created ad wrapper which sets some eventlistener on the WebGLRenderer:

import { useThree } from "@react-three/fiber";
import React from "react";
import { PropsWithChildren, useEffect, useRef, useState } from "react";
import * as THREE from "three";

const MovingImage: React.FC<PropsWithChildren> = ({ children }) => {
  const { raycaster, camera, gl } = useThree();
  const [dragging, setDragging] = useState(false);
  const meshRef = useRef<THREE.Mesh>(null);
  const [offset, setOffset] = useState(new THREE.Vector3());

  const onPointerDown = (event: PointerEvent) => {
    if (!meshRef.current) return;

    const rect = gl.domElement.getBoundingClientRect();
    const mouseX = ((event.clientX - rect.left) / rect.width) * 2 - 1;
    const mouseY = -((event.clientY - rect.top) / rect.height) * 2 + 1;

    raycaster.setFromCamera(new THREE.Vector2(mouseX, mouseY), camera);

    const planeIntersectPoint = new THREE.Vector3();
    if (
      raycaster.ray.intersectPlane(
        new THREE.Plane(new THREE.Vector3(0, 0, 1), -0),
        planeIntersectPoint
      )
    ) {
      setDragging(true);
      setOffset(meshRef.current.position.clone().sub(planeIntersectPoint));
    }
  };

  const onPointerMove = (event: PointerEvent) => {
    if (!dragging || !meshRef.current) return;

    const rect = gl.domElement.getBoundingClientRect();
    const mouseX = ((event.clientX - rect.left) / rect.width) * 2 - 1;
    const mouseY = -((event.clientY - rect.top) / rect.height) * 2 + 1;

    raycaster.setFromCamera(new THREE.Vector2(mouseX, mouseY), camera);

    const planeIntersectPoint = new THREE.Vector3();
    if (
      raycaster.ray.intersectPlane(
        new THREE.Plane(new THREE.Vector3(0, 0, 1), -0),
        planeIntersectPoint
      )
    ) {
      const position = planeIntersectPoint.add(offset);
      meshRef.current.position.copy(position);
    }
  };

  const onPointerUp = () => {
    setDragging(false);
  };

  useEffect(() => {
    gl.domElement.addEventListener("pointerdown", onPointerDown);
    gl.domElement.addEventListener("pointermove", onPointerMove);
    gl.domElement.addEventListener("pointerup", onPointerUp);

    return () => {
      gl.domElement.removeEventListener("pointerdown", onPointerDown);
      gl.domElement.removeEventListener("pointermove", onPointerMove);
      gl.domElement.removeEventListener("pointerup", onPointerUp);
    };
  }, [dragging]);

  return <mesh ref={meshRef}>{children}</mesh>;
};

export default MovingImage;

Is there a better way to implement the movement of a mesh inside a canvas?

 return <mesh ref={meshRef} pointerMove={onPointerMove} pointerDown={on Pointer Down} >{children}</mesh>;
};

Raycasting and pointer events are handled internally by r3f so it can be as simple as ^^^…

Thanks,
I already tried it that way, but this enables the moving of the mesh only by dragging on/from it, not by dragging outside as well.

your sandbox is private.

i suggest you make an invisible mesh as a hit area. imo registering events yourself is almost never needed, all it can do is waste cpu and create race conditions. if you already use intersectPlane to get around it, it would imo be cleaner to simply use an invisble plane hit area with simple onPointerOver etc.

<mesh>
  <planeGeometry />
  <meshBasicMaterial map={imageTexture} />
</mesh>
<mesh
  visible={false}
  onPointerDown={e => (e.stopPropagation(), e.target.setPointerCapture(e.pointerId))}
  onPointerMove={e => drag(e.unprojectedPoint)}
  onPointerUp={e => e.target.releasePointerCapture(e.pointerId)}>
  <planeGeometry />
</mesh>

this would also be more performant, since if you have dozens of images your code would register dozens of events, whereas fiber has a single pointerdown etc. and you can then also pair it with drei/Bvh to speed it up a thousandfold https://codesandbox.io/p/sandbox/bvh-txzeq8.

the raycaster also has a threshold for points/lines, i’m not sure if it also has that for meshes. but if i would be unsure if i register my own events or fork raycaster to get custom thresholds i would 100% do the latter.

here’s an example with draggable stuff https://codesandbox.io/p/sandbox/draggable-lines-yuyr6z

and another with physics https://codesandbox.io/p/sandbox/drag-n-drop-with-physics-and-collisions-9pjzm6 (the control in that example is drei/pivotcontrols, it uses invisible hit areas, that’s why you can steer even if slightly outside its meshes)

Oh sorry, I made it pubic.