使用 Matter.js 渲染到 DOM 或 React
Using Matter.js to render to the DOM or React
我想将自定义 HTML 元素呈现为 Matter.js 中的主体。我在 React 中使用它,这增加了一点复杂性,但它与我的问题无关。
我搜索了很多,我找到的唯一例子是 this one here,它似乎使用 querySelector
到 select 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 元素与物理世界交互(例如带有按钮的小盒子等)。
关于如何实现这一点有什么想法吗?或者,您能解释一下示例中使用的方法似乎已经成功了吗?
However, I cannot understand how to pass an HTML element inside the Bodies rectangle as in the example above.
这与示例的作用不完全相同。当 运行 无头时,Matter.js 处理物理而不知道它是如何渲染的。每个动画帧,您可以使用 MJS 主体的当前位置并重新定位您的元素(或绘制 canvas 等)以反映 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 改变了工作流程,但基本概念是相同的——MJS 主体数据从 MJS 后端单向流向渲染前端,因此从 MJS 的角度来看,一切都与香草相同上面的例子。大部分工作是正确设置 refs and useEffect
以便与 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, 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);
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.querySelector("#app"));
#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 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.18.0/matter.min.js"></script>
<div id="app"></div>
请注意,这些只是概念验证。在它们能够支持更多涉及的用例之前,可能需要更多的工作来设置抽象。
我想将自定义 HTML 元素呈现为 Matter.js 中的主体。我在 React 中使用它,这增加了一点复杂性,但它与我的问题无关。
我搜索了很多,我找到的唯一例子是 this one here,它似乎使用 querySelector
到 select 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 元素与物理世界交互(例如带有按钮的小盒子等)。
关于如何实现这一点有什么想法吗?或者,您能解释一下示例中使用的方法似乎已经成功了吗?
However, I cannot understand how to pass an HTML element inside the Bodies rectangle as in the example above.
这与示例的作用不完全相同。当 运行 无头时,Matter.js 处理物理而不知道它是如何渲染的。每个动画帧,您可以使用 MJS 主体的当前位置并重新定位您的元素(或绘制 canvas 等)以反映 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 改变了工作流程,但基本概念是相同的——MJS 主体数据从 MJS 后端单向流向渲染前端,因此从 MJS 的角度来看,一切都与香草相同上面的例子。大部分工作是正确设置 refs and useEffect
以便与 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, 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);
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.querySelector("#app"));
#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 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.18.0/matter.min.js"></script>
<div id="app"></div>
请注意,这些只是概念验证。在它们能够支持更多涉及的用例之前,可能需要更多的工作来设置抽象。