我是 Matter JS 的新手,所以请耐心等待。我从演示和其他来源整理了以下代码以满足我的需求:
function biscuits(width, height, items, gutter) {
const {
Engine,
Render,
Runner,
Composites,
MouseConstraint,
Mouse,
World,
Bodies,
} = Matter
const engine = Engine.create()
const world = engine.world
const render = Render.create({
element: document.getElementById('canvas'),
engine,
options: {
width,
height,
showAngleIndicator: true,
},
})
Render.run(render)
const runner = Runner.create()
Runner.run(runner, engine)
const columns = media({ bp: 'xs' }) ? 3 : 1
const stack = Composites.stack(
getRandom(gutter, gutter * 2),
gutter,
columns,
items.length,
0,
0,
(x, y, a, b, c, i) => {
const item = items[i]
if (!item) {
return null
}
const {
width: itemWidth,
height: itemHeight,
} = item.getBoundingClientRect()
const radiusAmount = media({ bp: 'sm' }) ? 100 : 70
const radius = item.classList.contains('is-biscuit-4')
? radiusAmount
: 0
const shape = item.classList.contains('is-biscuit-2')
? Bodies.circle(x, y, itemWidth / 2)
: Bodies.rectangle(x, y, itemWidth, itemHeight, {
chamfer: { radius },
})
return shape
}
)
World.add(world, stack)
function positionDomElements() {
Engine.update(engine, 20)
stack.bodies.forEach((block, index) => {
const item = items[index]
const xTrans = block.position.x - item.offsetWidth / 2 - gutter / 2
const yTrans = block.position.y - item.offsetHeight / 2 - gutter / 2
item.style.transform = `translate3d(${xTrans}px, ${yTrans}px, 0) rotate(${block.angle}rad)`
})
window.requestAnimationFrame(positionDomElements)
}
positionDomElements()
World.add(world, [
Bodies.rectangle(width / 2, 0, width, gutter, { isStatic: true }),
Bodies.rectangle(width / 2, height, width, gutter, { isStatic: true }),
Bodies.rectangle(width, height / 2, gutter, height, { isStatic: true }),
Bodies.rectangle(0, height / 2, gutter, height, { isStatic: true }),
])
const mouse = Mouse.create(render.canvas)
const mouseConstraint = MouseConstraint.create(engine, {
mouse,
constraint: {
stiffness: 0.2,
render: {
visible: false,
},
},
})
World.add(world, mouseConstraint)
render.mouse = mouse
Render.lookAt(render, {
min: { x: 0, y: 0 },
max: { x: width, y: height },
})
}
我有一个 HTML 链接列表,它模仿 Matter JS 中项目的移动(positionDomElements 函数)。我这样做是为了 SEO 目的,也是为了使导航易于访问和点击。
但是,因为我的画布位于 HTML 之上(不透明度为零),所以我需要能够使项目可单击和可拖动,以便我可以执行一些其他操作,例如导航到链接(和其他事件) ).
我不知道该怎么做。我四处寻找,但没有任何运气。
是否可以让每个项目可拖动(因为它已经是)并执行某种点击事件?
任何帮助或引导正确的方向将不胜感激。
您的任务似乎是将物理添加到一组 DOM 导航列表节点。您可能会认为,matter.js 需要提供画布才能运行,如果您想忽略它,则需要隐藏画布或将其不透明度设置为 0。
实际上,您可以使用自己的更新循环无头运行 MJS,而无需将元素注入到引擎中。实际上,不需要与
Matter.Render
或 Matter.Runner
相关的任何内容,您可以使用对 Matter.Engine.update(engine);
的调用使引擎在 requestAnimationFrame
循环中向前步进一个刻度。然后,您可以使用从 MJS 主体中提取的值来定位 DOM 元素。您已经在做这两件事,所以主要是剪切画布和渲染调用的问题。
这是一个可运行的示例,您可以参考并适应您的用例。
定位是难点;确保 MJS 坐标与您的鼠标和元素坐标相匹配需要一些麻烦。 MJS 将 x/y 坐标视为主体的中心,因此我使用
body.vertices[0]
表示左上角,这样可以更好地匹配 DOM。我想这些渲染决策中有很多都是特定于应用程序的,因此请将此视为概念验证。
const listEls = document.querySelectorAll("#mjs-wrapper li");
const engine = Matter.Engine.create();
const stack = Matter.Composites.stack(
// xx, yy, columns, rows, columnGap, rowGap, cb
0, 0, listEls.length, 1, 0, 0,
(xx, yy, i) => {
const {x, y, width, height} = listEls[i].getBoundingClientRect();
return Matter.Bodies.rectangle(x, y, width, height, {
isStatic: i === 0 || i + 1 === listEls.length
});
}
);
Matter.Composites.chain(stack, 0.5, 0, -0.5, 0, {
stiffness: 0.5,
length: 20
});
const mouseConstraint = Matter.MouseConstraint.create(
engine, {element: document.querySelector("#mjs-wrapper")}
);
Matter.Composite.add(engine.world, [stack, mouseConstraint]);
listEls.forEach(e => {
e.style.position = "absolute";
e.addEventListener("click", e =>
console.log(e.target.textContent)
);
});
(function update() {
requestAnimationFrame(update);
stack.bodies.forEach((block, i) => {
const li = listEls[i];
const {x, y} = block.vertices[0];
li.style.top = `${y}px`;
li.style.left = `${x}px`;
li.style.transform = `translate(-50%, -50%)
rotate(${block.angle}rad)
translate(50%, 50%)`;
});
Matter.Engine.update(engine);
})();
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html, body {
height: 100%;
}
body {
min-width: 600px;
}
#mjs-wrapper {
/* position this element */
margin: 1em;
height: 100%;
}
#mjs-wrapper ul {
font-size: 14pt;
list-style: none;
user-select: none;
position: relative;
}
#mjs-wrapper li {
background: #fff;
border: 1px solid #555;
display: inline-block;
padding: 1em;
cursor: move;
}
#mjs-wrapper li:hover {
background: #f2f2f2;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.20.0/matter.min.js"></script>
<div id="mjs-wrapper">
<ul>
<li>Foo</li>
<li>Bar</li>
<li>Baz</li>
<li>Quux</li>
<li>Garply</li>
<li>Corge</li>
</ul>
</div>