如何使项目可拖动和可点击?

问题描述

我是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,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,(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,itemWidth / 2)
        : Bodies.rectangle(x,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,width,{ isstatic: true }),Bodies.rectangle(width / 2,Bodies.rectangle(width,height / 2,Bodies.rectangle(0,])

  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导航列表节点中。您可能会觉得,需要为subject.js提供一个画布以起作用,如果您想忽略它,则有必要隐藏画布或将其不透明度设置为0。

实际上,您可以使用自己的更新循环无头运行MJS,而无需在引擎中注入任何元素。实际上,不需要任何与Matter.RenderMatter.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,listEls.length,1,(xx,i) => {
    const {x,y,width,height} = listEls[i].getBoundingClientRect();
    return Matter.Bodies.rectangle(x,height,{
      isStatic: i === 0 || i + 1 === listEls.length
    });
  }
);
Matter.Composites.chain(stack,0.5,-0.5,{
  stiffness: 0.5,length: 20
});
const mouseConstraint = Matter.MouseConstraint.create(
  engine,{element: document.querySelector("#mjs-wrapper")}
);
Matter.World.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.14.2/matter.min.js" integrity="sha512-pi0tSRZdlNRZeANPwdAIHRAYg6gZZV6QlAiyHXn5TYqLzBKE9jlttO/QgYLMhISD6oNv2kPsVelx+n5nw0FqKA==" crossorigin="anonymous"></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>