Matter.js —如何获取图像尺寸以设置实体大小? [英] Matter.js — How to get the dimension of an image to set Bodies sizes?

查看:40
本文介绍了Matter.js —如何获取图像尺寸以设置实体大小?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

伪代码

  • Array中的几个主体
  • 获取数组中每个图像的宽度和高度
  • 使用此值设置实体尺寸

代码

  var playA = Composites.stack(percentX(25)-assetSize/2%Y(25),1,6,5,5,函数(x,y){iA ++;var imgWidth;var imgHeight;var img = new Image();img.src =字符串(design [iA]);var imgWidth = 0;var imgHeight = 0;img.onload =函数a(){imgWidth = img.naturalWidth;imgHeight = img.naturalHeight;console.log(String(design [iA]),imgWidth,imgHeight);};console.log(String(design [iA]),imgHeight,imgWidth);//我无法在此处访问这些值.return Bodies.rectangle(x,y,imgWidth,imgHeight,{//碰撞过滤器:{group:group},摩擦力:1使成为: {雪碧:{纹理:设计[iA],xScale:(assetSize/100)* 0.46,yScale:(assetSize/100)* 0.46}}});});Composites.chain(playA,0.3,0,-0.5,0,{刚度:1长度:10,渲染:{类型:线",可见:假}}); 

解决方案

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

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

  var engine = Matter.Engine.create();var render = Matter.Render.create({元素:document.body,引擎:引擎,选项: {宽度:450,高度:250,线框:false,//图像必需}});Matter.Render.run(render);varRunner = Matter.Runner.create();Matter.Runner.run(runner,engine);var imgSizes = [[56,48],[45,50],[35,50],[60,63]];var stack = Matter.Composites.stack(//xx,yy,列,行,columnGap,rowGap,cb150、50、4、1、0、0,函数(x,y,i){var w = imgSizes [i] [0];var h = imgSizes [i] [1];返回Matter.Bodies.rectangle(x,y,w,h,{使成为: {雪碧:{纹理:"http://placekitten.com/" + w +"/" + h}}});});Matter.Composites.chain(stack,0.5,0,-0.5,0,{刚度:0.75,长度:10,渲染:{类型:线",可见值:true}});Matter.World.add(engine.world,[堆,Matter.Bodies.rectangle(225,0,450,25,{isStatic:正确}),Matter.Bodies.rectangle(450,150,25,300,{isStatic:正确}),Matter.Bodies.rectangle(0,150,25,300,{isStatic:正确}),Matter.Bodies.rectangle(225,250,450,25,{isStatic:正确})]);var mouse = Matter.Mouse.create(render.canvas);var mouseConstraint = Matter.MouseConstraint.create(engine,{鼠标:约束:{刚度:0.2,渲染:{visible:true}}});Matter.World.add(engine.world,mouseConstraint);render.mouse =鼠标;  

 < script src ="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.14.2/matter.min.js完整性=" sha512-pi0tSRZdlNRZeANPwdAIHRAYg6gZZV6QlAiyHXn5TYqLzBKE9jlttO/QgYLMhISD6oNv2kPsVelx + n5nw0FqKA =="crossorigin =" anonymous>><>

现在,如果您需要使用 onload 加载图像并使用其尺寸,则需要使用promise或将依赖于这些图像的 all 代码放入序列中规范

解决方法是:

由于您要处理多个 onload ,因此可以使 onload 合理化,使其更易于使用.我在此处

  var initializeMJS =函数(图像){var engine = Matter.Engine.create();var render = Matter.Render.create({元素:document.body,引擎:引擎,选项: {宽度:450,高度:250,线框:false,//图像必需}});Matter.Render.run(render);varRunner = Matter.Runner.create();Matter.Runner.run(runner,engine);var stack = Matter.Composites.stack(//xx,yy,列,行,columnGap,rowGap,cb150、50、4、1、0、0,函数(x,y,i){var w = images [i] .width;var h = images [i] .height;返回Matter.Bodies.rectangle(x,y,w,h,{使成为: {雪碧:{纹理:images [i] .src}}});});Matter.Composites.chain(stack,0.5,0,-0.5,0,{刚度:0.75,长度:10,渲染:{类型:线",可见值:true}});Matter.World.add(engine.world,[堆,Matter.Bodies.rectangle(225,0,450,25,{isStatic:正确}),Matter.Bodies.rectangle(450,150,25,300,{isStatic:正确}),Matter.Bodies.rectangle(0,150,25,300,{isStatic:正确}),Matter.Bodies.rectangle(225,250,450,25,{isStatic:正确})]);var mouse = Matter.Mouse.create(render.canvas);var mouseConstraint = Matter.MouseConstraint.create(engine,{鼠标:约束:{刚度:0.2,渲染:{visible:true}}});Matter.World.add(engine.world,mouseConstraint);render.mouse =鼠标;};var imageSizes = [[56,48],[45,50],[35,50],[60,63]];var imageURLs = imageSizes.map(function(e){返回"http://placekitten.com/" + e [0] +"/" + e [1];});Promise.all(imageURLs.map(function(e){返回新的Promise(函数(解决,拒绝){var img = new Image();img.onload = function(){解决(这个);};img.onerror =拒绝;img.src = e;})})).then(initializeMJS);  

 < script src ="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.14.2/matter.min.js"integrity =" sha512-pi0tSRZdlNRZeANPwdAIHRAYg6gZZV6QlAiyHXn5TYqLzBKE9jlttO/QgYLMhISD6oNv2kPsVelx + n5nw0FqKA =="crossorigin =" anonymous>><>

I am trying to programmatically set the width and heights of the chained bodies in matter.js. Unfortunately, I am only getting 0 as values and I am unsure why. My guess is that the images are not being loaded fast enough to provide those values. How can I load those dimensions before the images are loaded?

Pseudo-code

  • Several bodies from Array
  • Get the width and height of each image in the Array
  • Use this value to set the Bodies dimensions

Code

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

解决方案

If you know the dimensions and can populate an array beforehand, the solution is potentially straightforward since Matter.js loads images given a URL string, with the caveat that the engine doesn't wait for the loads before running.

Here's a minimal example of iterating over width/height pairs in an array and passing these properties into the rectangle calls which I'll use as a stepping stone to the example that matches your use case.

var engine = Matter.Engine.create();
var render = Matter.Render.create({
  element: document.body,
  engine: engine,
  options: {
    width: 450,
    height: 250,
    wireframes: false, // required for images
  }
});
Matter.Render.run(render);

var runner = Matter.Runner.create();
Matter.Runner.run(runner, engine);

var imgSizes = [[56, 48], [45, 50], [35, 50], [60, 63]];
var stack = Matter.Composites.stack(
  // xx, yy, columns, rows, columnGap, rowGap, cb
  150, 50, 4, 1, 0, 0,
  function (x, y, i) {
    var w = imgSizes[i][0];
    var h = imgSizes[i][1];
    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.World.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
  })
]);

var mouse = Matter.Mouse.create(render.canvas);
var mouseConstraint = Matter.MouseConstraint.create(engine, {
  mouse: mouse,
  constraint: {
    stiffness: 0.2,
    render: {visible: true}
  }
});
Matter.World.add(engine.world, mouseConstraint);
render.mouse = mouse;

<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.14.2/matter.min.js" integrity="sha512-pi0tSRZdlNRZeANPwdAIHRAYg6gZZV6QlAiyHXn5TYqLzBKE9jlttO/QgYLMhISD6oNv2kPsVelx+n5nw0FqKA==" crossorigin="anonymous"></script>

Now, if you need to load images using onload and use their dimensions, you'll need to use promises or put all code dependent on these images into the sequence of onload callback(s) as described in the canonical How do I return the response from an asynchronous call?.

The failing pattern is:

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

The fix is:

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`

Since you're juggling multiple onloads, you can promisify the onloads to make them easier to work with. I have a couple examples of doing this here and here agnostic of matter.js.

Here's an example of using promises to load images applied to your general problem. Again, I'll use my own code so that it's runnable and reproducible, but the pattern should be easy to extrapolate to your project.

The idea is to first load the images using a series of promises which are resolved when onload handlers fire, then use Promise.all to chain a then which runs the MJS initializer callback only when all images are loaded. The widths and heights are then accessible to your matter.js code within the callback.

As a side benefit, this ensures images are loaded by the time MJS runs.

var initializeMJS = function (images) {
  var engine = Matter.Engine.create();
  var render = Matter.Render.create({
    element: document.body,
    engine: engine,
    options: {
      width: 450,
      height: 250,
      wireframes: false, // required for images
    }
  });
  Matter.Render.run(render);

  var runner = Matter.Runner.create();
  Matter.Runner.run(runner, engine);

  var stack = Matter.Composites.stack(
    // xx, yy, columns, rows, columnGap, rowGap, cb
    150, 50, 4, 1, 0, 0,
    function (x, y, i) {
      var w = images[i].width;
      var h = images[i].height;
      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.World.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
    })
  ]);

  var mouse = Matter.Mouse.create(render.canvas);
  var mouseConstraint = Matter.MouseConstraint.create(engine, {
    mouse: mouse,
    constraint: {
      stiffness: 0.2,
      render: {visible: true}
    }
  });
  Matter.World.add(engine.world, mouseConstraint);
  render.mouse = mouse;
};

var imageSizes = [[56, 48], [45, 50], [35, 50], [60, 63]];
var imageURLs = imageSizes.map(function (e) {
  return "http://placekitten.com/" + e[0] + "/" + e[1];
});

Promise.all(imageURLs.map(function (e) {
    return new Promise(function (resolve, reject) {
      var img = new Image();
      img.onload = function () {
        resolve(this);
      };
      img.onerror = reject;
      img.src = e;
    })
  }))
  .then(initializeMJS)
;

<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.14.2/matter.min.js" integrity="sha512-pi0tSRZdlNRZeANPwdAIHRAYg6gZZV6QlAiyHXn5TYqLzBKE9jlttO/QgYLMhISD6oNv2kPsVelx+n5nw0FqKA==" crossorigin="anonymous"></script>

这篇关于Matter.js —如何获取图像尺寸以设置实体大小?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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