Utilities
Standalone helpers exported from @jolly-pixel/three.
Coordinates
projectToClient
function projectToClient(
camera: THREE.Camera,
canvas: ClientTarget,
point: THREE.Vector3Like
): THREE.Vector2Like | null;Projects a world point to client coordinates over canvas.getBoundingClientRect(), y pointing down. Use it to place HTML over a 3D view or to aim synthetic pointer events.
The camera's world matrices are refreshed first, so a camera moved since the last render projects from its new pose. Returns null when the point is outside the camera's near and far range, including behind it.
const anchor = projectToClient(camera, renderer.domElement, object.position);
if (anchor !== null) {
label.style.translate = `${anchor.x}px ${anchor.y}px`;
}clientToNdc
function clientToNdc(
canvas: ClientTarget,
clientX: number,
clientY: number,
target?: THREE.Vector2
): THREE.Vector2 | null;The inverse of projectToClient: maps client coordinates over canvas.getBoundingClientRect() to normalized device coordinates, -1 to 1 with y pointing up, ready for Raycaster.setFromCamera.
Points outside the canvas map past that range rather than being clamped, so a drag that leaves the canvas keeps tracking. The result is written into target when given, otherwise into a new Vector2. Returns null when the canvas has no area.
const pointer = new THREE.Vector2();
canvas.addEventListener("pointermove", (event) => {
if (clientToNdc(canvas, event.clientX, event.clientY, pointer) !== null) {
raycaster.setFromCamera(pointer, camera);
}
});ClientTarget
interface ClientTarget {
getBoundingClientRect(): Pick<
DOMRectReadOnly,
"left" | "top" | "width" | "height"
>;
}Any element works, a canvas included. Tests can pass a plain object returning fixed bounds.
Canvas
createCanvas2D
function createCanvas2D(
width: number,
height: number
): Canvas2D;
interface Canvas2D {
readonly canvas: HTMLCanvasElement;
readonly context: CanvasRenderingContext2D;
}Creates a canvas of the given size, in pixels, with its 2D context, for canvas-backed textures. Throws when no 2D context is available.
const { canvas, context } = createCanvas2D(64, 64);
context.fillRect(0, 0, 32, 32);
const texture = new THREE.CanvasTexture(canvas);