使用 Matter.js 渲染到 DOM 或 React [英] Using Matter.js to render to the DOM or React

查看:202
本文介绍了使用 Matter.js 渲染到 DOM 或 React的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

我搜索了很多,我找到的唯一例子是这里的这个,其中似乎使用 querySelector 来选择存在于 HTML 代码中的元素,然后以某种方式在矩形形状内使用它们.

似乎在做这项工作的部分如下:

var bodyDom = document.querySelectorAll('.block');var body = [];for (var i = 0, l = bodyDom.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);bodyDom[i].id = body.id;身体.推(身体);}World.add(engine.world, body);

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

但是,我无法理解如何在 Bodies 矩形内传递 HTML 元素,如上例所示.

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

关于如何实现这一目标的任何想法?或者,您能否解释一下示例中使用的方法似乎已经管理了它?

解决方案

但是,我无法理解如何在 Bodies 矩形内传递 HTML 元素,如上例所示.

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

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

这里有一个最小的例子,希望能更清楚地说明这一点:

const engine = Matter.Engine.create();常量框 = {身体:Matter.Bodies.rectangle(150, 0, 40, 40),elem: document.querySelector("#box"),使成为() {const {x, y} = this.body.position;this.elem.style.top = `${y - 20}px`;this.elem.style.left = `${x - 20}px`;this.elem.style.transform = `旋转(${this.body.angle}rad)`;},};const 地面 = Matter.Bodies.rectangle(200, 200, 400, 120, {isStatic: true});const mouseConstraint = Matter.MouseConstraint.create(引擎,{元素:document.body});物质.World.add(engine.world, [box.body, ground, mouseConstraint]);(函数重新渲染(){box.render();Matter.Engine.update(引擎);请求动画帧(重新渲染);})();

#box {位置:绝对;背景:#111;高度:40px;宽度:40px;光标:移动;}#地面 {位置:绝对;背景:#666;顶部:140px;高度:120px;宽度:400px;}html,正文{位置:相对;高度:100%;边距:0;}

<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.17.1/matter.min.js"></script><div id="box"></div><div id="ground"></div>

反应

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

#box {位置:绝对;背景:#111;高度:40px;宽度:40px;光标:移动;}#地面 {位置:绝对;顶部:140px;高度:120px;宽度:400px;背景:#666;}html,正文{位置:相对;高度:100%;边距:0;}

<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.17.1/matter.min.js"></script>

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

I want to render custom HTML elements as Bodies in Matter.js. I am using it in React which adds a bit of complexity but it's irrelevant to my issue.

I've searched a lot and the only example I found was this one here, which seems to use querySelector to select the elements that live in the HTML code then somehow use them inside the rectangle shapes.

The part that seems to be doing the job is the following:

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);

(the VIEW variables there could be just random numbers as they define the shape)

However, I cannot understand how to pass an HTML element inside the Bodies rectangle as in the example above.

Ideally, I want to have complex HTML elements interacting with the physics world (like a small box with buttons, etc).

Any ideas on how this could be achieved? Or, can you explain the method used in the example that seems to have managed it?

解决方案

However, I cannot understand how to pass an HTML element inside the Bodies rectangle as in the example above.

This isn't quite what the example does. When running headlessly, Matter.js handles the physics without having any idea how it's rendered. Per animation frame, you can use the current positions of the MJS bodies and reposition your elements (or draw on canvas, etc) to reflect MJS's view of the world.

Providing a root element to MJS does seem to break the one-way data flow, but this is only for informing MJS about events like mouse position and clicks--not to be confused with rendering.

Here's a minimal example to hopefully make this clearer:

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.World.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.17.1/matter.min.js"></script>
<div id="box"></div>
<div id="ground"></div>

React

React changes the workflow but the fundamental concept is the same--MJS body data flows one-directionally from the MJS back-end to the rendering front-end, so from MJS' perspective, everything is identical as the vanilla example above. Most of the work is setting up refs and useEffect properly for use with requestAnimationFrame.

#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 type="text/babel" defer>
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, 200, 400, 120, {isStatic: true}
    );
    const mouseConstraint = Matter.MouseConstraint.create(
      engine, {element: document.body}
    );
    Matter.World.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.render(<Scene />, document.body);

</script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.17.1/matter.min.js"></script>

Note that these are only proofs-of-concept. More work setting up abstractions will likely be necessary before they can support more involved use cases.

这篇关于使用 Matter.js 渲染到 DOM 或 React的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆