Map Custom Mouse Events to Matter.Mouse in Matter.js
This guide provides a straightforward method for mapping custom DOM
mouse or pointer events to a Matter.Mouse instance in
Matter.js. By intercepting events from an external DOM layer—such as a
UI overlay, an SVG viewport, or an offscreen target—and manually
updating the internal state of Matter.Mouse, you can drive
physics interactions and MouseConstraint behaviors
independently of default canvas listeners.
Understanding Matter.Mouse State
By default, Matter.Mouse.create(element) binds native
DOM listeners directly to the target canvas. The instance relies on
several core properties to synchronize physics interactions:
mouse.position: An{ x, y }coordinate object representing the current pointer location in world space.mouse.button: An integer indicating which mouse button is pressed (0for left,1for middle,2for right,-1for none).mouse.sourceEvents: References to native DOM events (mousedown,mousemove,mouseup).
When mapping custom DOM events, you can bypass the default listeners and feed values directly into these properties.
Step 1: Create an Unbound or Detached Mouse
Instantiate the Mouse object using an element that does
not conflict with your main canvas, or clear the default listeners from
the canvas by detaching them.
const { Engine, Render, Runner, Bodies, Composite, Mouse, MouseConstraint } = Matter;
const engine = Engine.create();
const render = Render.create({
element: document.body,
engine: engine,
canvas: document.getElementById('physics-canvas')
});
// Create the mouse bound to the physics canvas initially
const mouse = Mouse.create(render.canvas);
// Remove default event listeners to prevent duplicate processing
render.canvas.removeEventListener('mousedown', mouse.mousedown);
render.canvas.removeEventListener('mousemove', mouse.mousemove);
render.canvas.removeEventListener('mouseup', mouse.mouseup);
render.canvas.removeEventListener('mousewheel', mouse.mousewheel);
// Attach the MouseConstraint to the engine using this mouse instance
const mouseConstraint = MouseConstraint.create(engine, {
mouse: mouse,
constraint: {
stiffness: 0.2,
render: { visible: true }
}
});
Composite.add(engine.world, mouseConstraint);Step 2: Forward Custom DOM Events
Identify your custom DOM element (for example, an overlay
div or SVG element) and bind your custom event handlers to
update the Matter.Mouse instance manually.
const customOverlay = document.getElementById('custom-overlay');
function getRelativeCoordinates(event, targetElement) {
const rect = targetElement.getBoundingClientRect();
return {
x: event.clientX - rect.left,
y: event.clientY - rect.top
};
}
// Handle pointer movement
customOverlay.addEventListener('pointermove', (event) => {
const coords = getRelativeCoordinates(event, render.canvas);
mouse.position.x = coords.x;
mouse.position.y = coords.y;
mouse.sourceEvents.mousemove = event;
});
// Handle pointer down
customOverlay.addEventListener('pointerdown', (event) => {
const coords = getRelativeCoordinates(event, render.canvas);
mouse.position.x = coords.x;
mouse.position.y = coords.y;
mouse.button = event.button;
mouse.sourceEvents.mousedown = event;
});
// Handle pointer up
customOverlay.addEventListener('pointerup', (event) => {
mouse.button = -1;
mouse.sourceEvents.mouseup = event;
});Step 3: Handle Pixel Ratio and Canvas Scaling
If your renderer scales the canvas via CSS or handles high-DPI
displays with a pixelRatio, adjust the coordinates before
assigning them to mouse.position:
function getScaledCoordinates(event, canvas) {
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
const scaleY = canvas.height / rect.height;
return {
x: (event.clientX - rect.left) * scaleX,
y: (event.clientY - rect.top) * scaleY
};
}Assigning the calculated scaleX and scaleY
values ensures the world coordinates accurately align with the custom
element's viewport positions.