【问题标题】:Matter.js — How to get the dimension of an image to set Bodies sizes?Matter.js — 如何获取图像的尺寸来设置身体尺寸?
【发布时间】:2020-10-15 12:25:05
【问题描述】:

我正在尝试以编程方式设置 matte.js 中链式主体的宽度和高度。不幸的是,我只得到 0 作为值,我不确定为什么。我的猜测是图像加载速度不够快,无法提供这些值。如何在加载图像之前加载这些尺寸?

伪代码

  • Array 中的几个物体
  • 获取Array中每张图片的宽高
  • 使用此值设置实体尺寸

代码

var playA = Composites.stack(
  percentX(25) - assetSize / 2,
  percentY(25),
  1,
  6,
  5,
  5,
  function (x, y) {
    iA++;

    var imgWidth;
    var imgHeight;

    var img = new Image();
    img.src = String(design[iA]);

    var imgWidth = 0;
    var imgHeight = 0;

    img.onload = function a() {
      imgWidth = img.naturalWidth;
      imgHeight = img.naturalHeight;

      console.log(String(design[iA]), imgWidth, imgHeight);
    };
    console.log(String(design[iA]), imgHeight, imgWidth); // I can't access the values here.

    return Bodies.rectangle(x, y, imgWidth, imgHeight, {
      // collisionFilter: { group: group },
      friction: 1,
      render: {
        sprite: {
          texture: design[iA],
          xScale: (assetSize / 100) * 0.46,
          yScale: (assetSize / 100) * 0.46
        }
      }
    });
  }
);

Composites.chain(playA, 0.3, 0, -0.5, 0, {
  stiffness: 1,
  length: 10,
  render: { type: "line", visible: false }
});

【问题讨论】:

    标签: javascript image matter.js


    【解决方案1】:

    如果您知道尺寸并且可以预先填充数组,那么解决方案可能很简单,因为 Matter.js 会加载给定 URL 字符串的图像,但需要注意的是引擎在运行之前不会等待加载。

    这是一个迭代数组中宽度/高度对并将这些属性传递到 rectangle 调用的最小示例,我将使用它作为与您的用例匹配的示例的垫脚石。

    const engine = Matter.Engine.create();
    const render = Matter.Render.create({
      element: document.body,
      engine: engine,
      options: {
        width: 450,
        height: 250,
        wireframes: false, // required for images
      }
    });
    Matter.Render.run(render);
    
    const runner = Matter.Runner.create();
    Matter.Runner.run(runner, engine);
    
    const imgSizes = [[56, 48], [45, 50], [35, 50], [60, 63]];
    const stack = Matter.Composites.stack(
      // xx, yy, columns, rows, columnGap, rowGap, cb
      150, 50, 4, 1, 0, 0,
      (x, y, i) => {
        const [w, h] = imgSizes[i];
        return Matter.Bodies.rectangle(x, y, w, h, {
          render: {
            sprite: {
              texture: `http://placekitten.com/${w}/${h}` 
            }
          }
        });
      }
    );
    Matter.Composites.chain(stack, 0.5, 0, -0.5, 0, {
      stiffness: 0.75,
      length: 10,
      render: {type: "line", visible: true}
    });
    
    Matter.Composite.add(engine.world, [
      stack,
      Matter.Bodies.rectangle(225, 0, 450, 25, {
        isStatic: true
      }),
      Matter.Bodies.rectangle(450, 150, 25, 300, {
        isStatic: true
      }),
      Matter.Bodies.rectangle(0, 150, 25, 300, {
        isStatic: true
      }),
      Matter.Bodies.rectangle(225, 250, 450, 25, {
        isStatic: true
      })
    ]);
    
    const mouse = Matter.Mouse.create(render.canvas);
    const mouseConstraint = Matter.MouseConstraint.create(engine, {
      mouse: mouse,
      constraint: {
        stiffness: 0.2,
        render: {visible: true}
      }
    });
    Matter.Composite.add(engine.world, mouseConstraint);
    render.mouse = mouse;
    <script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.18.0/matter.min.js"></script>

    现在,如果您需要使用onload 加载图像并使用它们的尺寸,则需要使用承诺或将依赖于这些图像的所有代码放入onload 回调的序列中(s) 如规范 How do I return the response from an asynchronous call? 中所述。

    失败的模式是:

    const getSomethingAsync = cb => setTimeout(() => cb("something"), 0);
    
    let data = null;
    getSomethingAsync(result => {
      data = result;
      console.log("this runs last");
    });
    console.log(data); // guaranteed to be null, not "something"
    // more logic that is supposed to depend on data

    解决方法是:

    const getSomethingAsync = cb => setTimeout(() => cb("something"), 0);
    
    getSomethingAsync(data => {
      console.log(data);
      // logic that depends on the data from `getSomethingAsync`
    });
    
    console.log("this will run first");
    // logic that doesn't depend on data from `getSomethingAsync`

    由于您要处理多个onloads,您可以承诺onloads 以使其更易于使用。我有几个这样做的例子 herehere 与 matter.js 无关。

    这是一个使用 Promise 加载应用于一般问题的图像的示例。同样,我将使用我自己的代码,以便它可运行和可重现,但该模式应该很容易外推到您的项目中。

    这个想法是首先使用一系列承诺加载图像,这些承诺在onload 处理程序触发时解决,然后使用Promise.all 链接then,该then 仅在加载所有图像时运行MJS 初始化程序回调。然后,您的 matter.js 代码可以在回调中访问宽度和高度。

    作为附带的好处,这可以确保在 MJS 运行时加载图像。

    const initializeMJS = images => {
      const engine = Matter.Engine.create();
      const render = Matter.Render.create({
        element: document.body,
        engine: engine,
        options: {
          width: 450,
          height: 250,
          wireframes: false, // required for images
        }
      });
      Matter.Render.run(render);
    
      const runner = Matter.Runner.create();
      Matter.Runner.run(runner, engine);
    
      const stack = Matter.Composites.stack(
        // xx, yy, columns, rows, columnGap, rowGap, cb
        150, 50, 4, 1, 0, 0,
        (x, y, i) => {
          const {width: w, height: h} = images[i];
          return Matter.Bodies.rectangle(x, y, w, h, {
            render: {
              sprite: {
                texture: images[i].src
              }
            }
          });
        }
      );
      Matter.Composites.chain(stack, 0.5, 0, -0.5, 0, {
        stiffness: 0.75,
        length: 10,
        render: {type: "line", visible: true}
      });
    
      Matter.Composite.add(engine.world, [
        stack,
        Matter.Bodies.rectangle(225, 0, 450, 25, {
          isStatic: true
        }),
        Matter.Bodies.rectangle(450, 150, 25, 300, {
          isStatic: true
        }),
        Matter.Bodies.rectangle(0, 150, 25, 300, {
          isStatic: true
        }),
        Matter.Bodies.rectangle(225, 250, 450, 25, {
          isStatic: true
        })
      ]);
    
      const mouse = Matter.Mouse.create(render.canvas);
      const mouseConstraint = Matter.MouseConstraint.create(engine, {
        mouse: mouse,
        constraint: {
          stiffness: 0.2,
          render: {visible: true}
        }
      });
      Matter.Composite.add(engine.world, mouseConstraint);
      render.mouse = mouse;
    };
    
    const imageSizes = [[56, 48], [45, 50], [35, 50], [60, 63]];
    const imageURLs = imageSizes.map(([w, h]) =>
      `http://placekitten.com/${w}/${h}`
    );
    
    Promise.all(imageURLs.map(e =>
        new Promise((resolve, reject) => {
          const img = new Image();
          img.onload = () => resolve(img);
          img.onerror = reject;
          img.src = e;
        })
      ))
      .then(initializeMJS)
    ;
    <script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.18.0/matter.min.js"></script>

    【讨论】:

    • 首先是@ggorlen。感谢您的详细解释,并花时间添加一个真实的例子。我会尽快对此进行测试。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-07
    • 2014-03-07
    • 2011-08-03
    • 1970-01-01
    • 2011-10-14
    • 2015-08-28
    相关资源
    最近更新 更多