Kokodoko
Kokodoko

Reputation: 28128

Why do MatterJS physics coordinates have offset with DOM elements?

I'm running a MatterJS physics simulation by adding boxes to the physics engine, and at the same time creating a DOM element with the same dimensions.

Then, I'm looping through the physics boxes to get their coordinates. I then use those coordinates to position the corresponding DOM element. This seems to work correctly, except that my static "ground" box seems to have a 10 pixel invisible radius around it?

My boxes are 40x40 pixels. The ground is at 400 pixels. The boxes stop falling at 350 pixels. So why the 10 pixels discrepancy?

Here is an example of the issue in CodePen

EDIT: if I render the same boxes in a canvas, the discrepancy isn't there. Codepen Canvas

class Box {
  constructor(world, x, y, w, h, options) {
    // add box to physics simulation
    this.physicsBox = Matter.Bodies.rectangle(x, y, w, h, options);
    Matter.Composite.add(world, this.physicsBox);

    // add DOM box at the same coordinates
    this.div = document.createElement("box");
    document.body.appendChild(this.div);
    this.div.style.width = w + "px";
    this.div.style.height = h + "px";
    this.update();
  }

  update() {
    let pos = this.physicsBox.position;
    let angle = this.physicsBox.angle;
    let degrees = angle * (180 / Math.PI);
    this.div.style.transform = `translate(${pos.x}px, ${pos.y}px) rotate(${degrees}deg)`;
  }
}

let engine;
let boxes = [];

setupMatter();
addObjects();
gameLoop();

function setupMatter() {
  engine = Matter.Engine.create();
}

function addObjects() {
  boxes.push(
    new Box(engine.world, 250, 20, 40, 40),
    new Box(engine.world, 300, 350, 40, 40),
    new Box(engine.world, 320, 70, 40, 40),
    new Box(engine.world, 0, 400, 800, 60, { isStatic: true })
  );
}

function gameLoop() {
  // update the physics world
  Matter.Engine.update(engine, 1000 / 60);
  // update the DOM world
  for (let b of boxes) {
    b.update();
  }
  requestAnimationFrame(() => this.gameLoop());
}
html,
body {
  background-color: #4b4b4b;
  color: white;
  margin: 0px;
  padding: 0px;
}

box {
  margin: 0px;
  padding: 0px;
  box-sizing: border-box;
  position: absolute;
  display: block;
  background-color: rgba(45, 188, 232, 0.687);
  transform-origin: 20px 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.17.1/matter.min.js"></script>

Upvotes: 2

Views: 779

Answers (1)

Chris Lear
Chris Lear

Reputation: 6742

The problem is that when you set your x,y positions for the box, you didn't take into account the heights. So the displayed positions didn't match the actual positions.

The only line I've really changed is this.div.style.transform = `translate(${pos.x}px, ${pos.y}px) rotate(${degrees}deg)`;.

The 10-pixel discrepancy was caused because the 60px-deep base was being displayed 30px (half the depth) too low, while the 40px-deep boxes were being displayed 20px too low - net 10px above the base. It all looked OK when I tried setting the heights to 1px, which was how I discovered the bug.

class Box {
  constructor(world, x, y, w, h, options) {
    // add box to physics simulation
    this.physicsBox = Matter.Bodies.rectangle(x, y, w, h, options);
    Matter.Composite.add(world, this.physicsBox);

    // add DOM box at the same coordinates
    this.div = document.createElement("box");
    document.body.appendChild(this.div);
    this.div.style.width = w + "px";
    this.div.style.height = h + "px";
    this.width = w;
    this.height = h;
    this.update();
  }

  update() {
    let pos = this.physicsBox.position;
    let angle = this.physicsBox.angle;
    let degrees = angle * (180 / Math.PI);
    this.div.style.transform = `translate(${pos.x - (this.width/2)}px, ${pos.y-(this.height/2)}px) rotate(${degrees}deg)`;
  }
}

let engine;
let boxes = [];

setupMatter();
addObjects();
gameLoop();

function setupMatter() {
  engine = Matter.Engine.create();
}

function addObjects() {
  boxes.push(
    new Box(engine.world, 250, 20, 40, 40),
    new Box(engine.world, 300, 350, 40, 40),
    new Box(engine.world, 320, 70, 40, 40),
    new Box(engine.world, 0, 400, 800, 60, { isStatic: true })
  );
}

function gameLoop() {
  // update the physics world
  Matter.Engine.update(engine, 1000 / 60);
  // update the DOM world
  for (let b of boxes) {
    b.update();
  }
  requestAnimationFrame(() => this.gameLoop());
}
html,
body {
  background-color: #4b4b4b;
  color: white;
  margin: 0px;
  padding: 0px;
}

box {
  margin: 0px;
  padding: 0px;
  box-sizing: border-box;
  position: absolute;
  display: block;
  background-color: rgba(45, 188, 232, 0.687);
  transform-origin: 20px 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.17.1/matter.min.js"></script>

Upvotes: 2

Related Questions