I'm trying to use Matter.js for it's physics engine while using D3.js for rendering the Matter physics bodies as SVG elements.
This demo is just a box
dropping onto the ground
. Everything works as expected up until I try to add a MouseConstraint. My mouse.element
is the SVG viewbox, which is good, but clicking on the box
doesn't do anything. The code below is in this codepen as well.
I made a very similar working example in another codepen using Matter's default renderer (which uses canvas) where the mouse interactions "just work" and you can click/drag the box around.
TLDR: What leg-work do I need to do here to make the MouseConstraint work without Matter's renderer? I don't use Javascript in my day-to-day (besides some D3) so this is all a little opaque to me.
Here's the non-working code:
const Engine = Matter.Engine
const Bodies = Matter.Bodies
const Composite = Matter.Composite
const World = Matter.World
const width = 400;
const height = 400;
// Helpers
vertices_to_points = function(vertices) {
return vertices.map(vertex => `${vertex.x},${vertex.y}`).join(" ");
}
// Initialize D3 selection
const svg = d3.select("#container")
.append("svg")
.attr("viewBox", [0, 0, width, height])
.attr("style", "max-width: 100%; height: auto;");
// Initialize physics objects
const engine = Engine.create();
const world = engine.world;
const bodies = world.bodies;
const box = Bodies.rectangle(width/2, height/2, 100, 100);
const ground = Bodies.rectangle(width/2, height - 25, width - 10, 40, { isStatic: true });
Composite.add(engine.world, [box, ground]);
// Render
bodies.forEach(body => {
svg.append("polygon")
.attr("id", `body-${body.id}`)
.attr("points", vertices_to_points(body.vertices))
.attr("fill", "white")
.attr("stroke", "black")
.attr("stroke-width", 2)
});
// Update + render loop
(function update() {
window.requestAnimationFrame(update);
Engine.update(engine, 5);
// Move the vertices to sync the D3 <polygon> with the Matter
// physics body.
bodies.forEach(body => {
svg.select(`#body-${body.id}`)
.attr("points", vertices_to_points(body.vertices))
});
})();
// Mouse interaction (DOES NOT WORK)
const mouse = Matter.Mouse.create(svg.node());
mouse.pixelRatio = 2;
const mouse_constraint = Matter.MouseConstraint.create(engine, {
mouse: mouse
})
World.add(world, mouse_constraint);
Use the container for the mouse constraint:
const mouse = Matter.Mouse.create(document.querySelector("#container"));
If you only want the mouse to affect certain bodies, try masks.
Note that World
is deprecated, so you can use Composite
instead.