Matter.js —如何获取图像尺寸以设置实体大小? [英] Matter.js — How to get the dimension of an image to set Bodies sizes?
问题描述
我正在尝试以编程方式在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 代码放入序列中规范
const getSomethingAsync = cb =>setTimeout(()=> cb("something"),0);让数据=空;getSomethingAsync(result => {数据=结果;console.log(这最后运行");});console.log(data);//保证为null,而不是"something"//应该依赖于数据的更多逻辑
解决方法是:
const getSomethingAsync = cb =>setTimeout(()=> cb("something"),0);getSomethingAsync(data => {console.log(data);//依赖于`getSomethingAsync`中的数据的逻辑});console.log(这将首先运行");//不依赖于`getSomethingAsync`
中的数据的逻辑
由于您要处理多个 onload
,因此可以使 onload
合理化,使其更易于使用.我在此处和 Promise.all
链接一个随后运行的
仅在加载所有图像时,MJS初始化程序回调才起作用.宽度和高度随后将在回调中供您的subject.js代码访问.
作为附带的好处,这可以确保在MJS运行时加载图像.
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 onload
s, you can promisify the onload
s 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屋!