一键加载图像 [英] Images onload in one function

查看:93
本文介绍了一键加载图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试学习JavaScript,这是我的第一个游戏.如何在一个函数中使所有图像 onload ,然后在画布上绘制它们以使代码更短?

I'm trying to learn JavaScript, making my first game. How I can make all images onload in one function and later draw it in the canvas making my code shorter?

我如何将大量图像放置在数组中,然后再将其放入函数中.

How can I put a lot of images in an array and later us it in a function.

这是我学习JavaScript的第三天.

This is my third day of learning JavaScript.

谢谢.

var cvs = document.getElementById('canvas');
var ctx = cvs.getContext('2d');

//load images

var bird = new Image();
var bg = new Image();
var fg = new Image();
var pipeNorth = new Image();
var pipeSouth = new Image();

//images directions 
bg.src = "assets/bg.png";
bird.src = "assets/bird.png";
fg.src = "assets/fg.png";
pipeNorth.src = "assets/pipeNorth.png";
pipeSouth.src = "assets/pipeSouth.png";

var heightnum = 80;
var myHeight = pipeSouth.height+heightnum;
var bX = 10;
var bY = 150;
var gravity = 0.5;

// Key Control :D
 document.addEventListener("keydown",moveUP)
function moveUP(){
bY -= 20;
}
//pipe coordinates

var pipe = [];

pipe[0] = {
  x : cvs.width,
  y : 0
}
//draw images 


//Background img
  bg.onload = function back(){
    ctx.drawImage(bg,0,0);

  }
  //pipe north
  pipeNorth.onload = function tubo(){

    for(var i = 0; i < pipe.length; i++){

    ctx.drawImage(pipeNorth,pipe[i].x,pipe[i].y);
    pipe[i].x--;
    }
  }

  pipeSouth.onload = function tuba(){
    ctx.drawImage(pipeSouth,pipe[i].x,pipe[i].y+myHeight);

  }



  bird.onload = function pajaro(){
    ctx.drawImage(bird,bX,bY);
    bY += gravity;

    requestAnimationFrame(pajaro);  
  } 


  fg.onload = function flor(){
    ctx.drawImage(fg,0,cvs.height - fg.height);

  }




moveUP();
   back();
   tuba();
   pajaro();
   flor();

推荐答案

这可以通过

This can be done with Promise.all. We'll make a new promise for each image we want to load, resolving when onload is called. Once Promise.all resolves, we can call our initialize function and continue on with the rest of our logic. This avoids race conditions where the main game loop's requestAnimationFrame is called from bird.onload, but it's possible that pipe entities and so forth haven't loaded yet.

这是一个最小的完整示例:

Here's a minimal, complete example:

const initialize = images => {

  // images are loaded here and we can go about our business
  
  const canvas = document.createElement("canvas");
  document.body.appendChild(canvas);
  canvas.width = 400;
  canvas.height = 200;
  const ctx = canvas.getContext("2d");

  Object.values(images).forEach((e, i) =>
    ctx.drawImage(e, i * 100, 0)
  );
};

const imageUrls = [
  "http://placekitten.com/90/100",
  "http://placekitten.com/90/130",
  "http://placekitten.com/90/160",      
  "http://placekitten.com/90/190",
];

Promise.all(imageUrls.map(e =>
  new Promise((resolve, reject) => {
    const img = new Image();
    img.onload = () => resolve(img);
    img.onerror = reject;
    img.src = e;
  })
)).then(initialize);

请注意,我在上面的示例中使用了一个数组来存储图像.解决的问题是

Notice that I used an array in the above example to store the images. The problem this solves is that the

var foo = ...
var bar = ...
var baz = ...
var qux = ...
foo.src = ...
bar.src = ...
baz.src = ...
qux.src = ...
foo.onload = ...
bar.onload = ...
baz.onload = ...
qux.onload = ...

模式非常难以管理和扩展.如果您决定在游戏中添加其他内容,则需要重新编写代码以解决该问题,并且游戏逻辑变得非常湿.错误变得难以发现和消除.另外,如果我们想要一个特定的图像,我们更喜欢像 images.bird 那样访问它,而不是 images [1] ,这样可以保留各个变量的语义,但是例如,使我们能够遍历对象并调用每个实体的 render 函数.

pattern is extremely difficult to manage and scale. If you decide to add another thing into the game, then the code needs to be re-written to account for it and game logic becomes very wet. Bugs become difficult to spot and eliminate. Also, if we want a specific image, we'd prefer to access it like images.bird rather than images[1], preserving the semantics of the individual variables, but giving us the power to loop through the object and call each entity's render function, for example.

所有这些促使对象聚合游戏实体.我们希望每个实体拥有的一些信息可能包括,例如,实体的当前位置,死/活状态,移动和呈现它的功能等.

All of this motivates an object to aggregate game entities. Some information we'd like to have per entity might include, for example, the entity's current position, dead/alive status, functions for moving and rendering it, etc.

拥有某种包含所有初始游戏状态(通常是外部JSON文件)的单独的原始数据对象也是一个好主意.

It's also a nice idea to have some kind of separate raw data object that contains all of the initial game state (this would typically be an external JSON file).

很显然,这可以成为重要的重构,但是当游戏的规模变得越来越小时(这可以逐步采用这些设计思想),这是必不可少的步骤.通常,最好是先硬着头皮.

Clearly, this can turn into a significant refactor, but it's a necessary step when the game grows beyond small (and we can incrementally adopt these design ideas). It's generally a good idea to bite the bullet up front.

这是一个概念证明,上面阐释了一些沉思.希望这为您如何管理游戏状态和逻辑提供了一些思路.

Here's a proof-of-concept illustrating some of the the musings above. Hopefully this offers some ideas for how you might manage game state and logic.

const entityData = [
  {
    name: "foo", 
    path: "http://placekitten.com/80/80",
    x: 0,
    y: 0
  },
  {
    name: "baz", 
    path: "http://placekitten.com/80/150",
    x: 0,
    y: 90
  },
  {
    name: "quux", 
    path: "http://placekitten.com/100/130",
    x: 90,
    y: 110
  },
  {
    name: "corge", 
    path: "http://placekitten.com/200/240",
    x: 200,
    y: 0
  },
  {
    name: "bar",
    path: "http://placekitten.com/100/100",
    x: 90,
    y: 0
  }
  /* you can add more properties and functions 
     (movement, etc) to each entity
     ... try adding more entities ...
  */
];

const entities = entityData.reduce((a, e) => {
  a[e.name] = {...e, image: new Image(), path: e.path};
  return a;
}, {});

const initialize = () => {
  const canvas = document.createElement("canvas");
  document.body.appendChild(canvas);
  canvas.width = innerWidth;
  canvas.height = innerHeight;
  const ctx = canvas.getContext("2d");

  for (const key of Object.keys(entities)) {
    entities[key].alpha = Math.random();
  }
  
  (function render () {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
  
    Object.values(entities).forEach(e => {
      ctx.globalAlpha = Math.abs(Math.sin(e.alpha += 0.005));
      ctx.drawImage(e.image, e.x, e.y);
      ctx.globalAlpha = 1;
    });
    
    requestAnimationFrame(render);
  })();
};

Promise.all(Object.values(entities).map(e =>
    new Promise((resolve, reject) => {
      e.image.onload = () => resolve(e.image);
      e.image.onerror = () => 
        reject(`${e.path} failed to load`)
      ;
      e.image.src = e.path;
    })
  ))
  .then(initialize)
  .catch(err => console.error(err))
;

这篇关于一键加载图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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