使用 Matter.js 渲染到 DOM 或 React

我想在 Matter.js 中将自定义 HTML 元素呈现为主体。我在 React 中使用它,这增加了一些复杂性,但这与我的问题无关。

我进行了很多搜索,发现的唯一示例是 this one here ,它似乎使用 querySelector 来选择存在于 HTML 代码中的元素,然后以某种方式在矩形形状中使用它们。

似乎正在完成这项工作的部分如下:

var bodiesDom = document.querySelectorAll('.block');
var bodies = [];
for (var i = 0, l = bodiesDom.length; i < l; i++) {
    var body = Bodies.rectangle(
        VIEW.centerX,
        20, 
        VIEW.width*bodiesDom[i].offsetWidth/window.innerWidth, 
        VIEW.height*bodiesDom[i].offsetHeight/window.innerHeight
    );
    bodiesDom[i].id = body.id;
    bodies.push(body);
}
World.add(engine.world, bodies);

VIEW 变量在定义形状时可能只是随机数)

但是,我无法理解如何在上面的示例中传递 Bodies 矩形内的 HTML 元素。

理想情况下,我想让复杂的 HTML 元素与物理世界交互(比如一个带按钮的小盒子等)。

关于如何实现这一点的任何想法?或者,您能解释一下示例中使用的似乎已经成功的方法吗?

stack overflow Using Matter.js to render to the DOM or React
原文答案
author avatar

接受的答案

但是,我无法理解如何像上面的示例那样在 Bodies 矩形内传递 HTML 元素。

这不是这个例子所做的。当无头运行时,Matter.js 处理物理而不知道它是如何呈现的。每个动画帧,您可以使用 MJS 主体的当前位置并重新定位您的元素(或在画布上绘制等)以反映 MJS 的世界观。

向 MJS 提供根元素似乎会破坏单向数据流,但这只是为了通知 MJS 有关鼠标位置和点击等事件的信息——不要与渲染混淆。

这是一个希望能使这一点更清楚的最小示例:

const engine = Matter.Engine.create();  
const box = {
  body: Matter.Bodies.rectangle(150, 0, 40, 40),
  elem: document.querySelector("#box"),
  render() {
    const {x, y} = this.body.position;
    this.elem.style.top = `${y - 20}px`;
    this.elem.style.left = `${x - 20}px`;
    this.elem.style.transform = `rotate(${this.body.angle}rad)`;
  },
};
const ground = Matter.Bodies.rectangle(
  200, 200, 400, 120, {isStatic: true}
);
const mouseConstraint = Matter.MouseConstraint.create(
  engine, {element: document.body}
);
Matter.Composite.add(
  engine.world, [box.body, ground, mouseConstraint]
);
(function rerender() {
  box.render();
  Matter.Engine.update(engine);
  requestAnimationFrame(rerender);
})();
#box {
  position: absolute;
  background: #111;
  height: 40px;
  width: 40px;
  cursor: move;
}

#ground {
  position: absolute;
  background: #666;
  top: 140px;
  height: 120px;
  width: 400px;
}

html, body {
  position: relative;
  height: 100%;
  margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.18.0/matter.min.js"></script>
<div id="box"></div>
<div id="ground"></div>

react

React 改变了工作流程,但基本概念是相同的——MJS 主体数据从 MJS 后端单向流向渲染前端,因此从 MJS 的角度来看,一切都与上面的普通示例相同。大部分工作是正确设置 refsuseEffect 以便与 requestAnimationFrame 一起使用。

const {Fragment, useEffect, useRef} = React;

const Scene = () => {
  const requestRef = useRef();
  const boxRef = useRef();
  const groundRef = useRef();

  const animate = () => {
    const engine = Matter.Engine.create();

    const box = {
      body: Matter.Bodies.rectangle(150, 0, 40, 40),
      elem: boxRef.current,
      render() {
        const {x, y} = this.body.position;
        this.elem.style.top = `${y - 20}px`;
        this.elem.style.left = `${x - 20}px`;
        this.elem.style.transform = `rotate(${this.body.angle}rad)`;
      },
    };
    const ground = Matter.Bodies.rectangle(
      200, // x
      200, // y
      400, // w
      120, // h
      {isStatic: true}
    );
    const mouseConstraint = Matter.MouseConstraint.create(
      engine,
      {element: document.body}
    );
    Matter.Composite.add(engine.world, [
      box.body,
      ground,
      mouseConstraint,
    ]);

    (function rerender() {
      box.render();
      Matter.Engine.update(engine);
      requestRef.current = requestAnimationFrame(rerender);
    })();
  };

  useEffect(() => {
    animate();
    return () => cancelAnimationFrame(requestRef.current);
  }, []);

  return (
    <Fragment>
      <div id="box" ref={boxRef}></div>
      <div id="ground" ref={groundRef}></div>
    </Fragment>
  );
};

ReactDOM.createRoot(document.querySelector("#app")).render(
  <Scene />
);
#box {
  position: absolute;
  background: #111;
  height: 40px;
  width: 40px;
  cursor: move;
}

#ground {
  position: absolute;
  top: 140px;
  height: 120px;
  width: 400px;
  background: #666;
}

html, body {
  position: relative;
  height: 100%;
  margin: 0;
}
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.18.0/matter.min.js"></script>
<div id="app"></div>

请注意,这些只是概念验证。在它们能够支持更多涉及的用例之前,可能需要更多的工作来设置抽象。


答案: