javascript - 在一个函数中加载图像

标签 javascript html arrays canvas onload-event

我正在尝试学习 JavaScript,制作我的第一个游戏。如何使所有图像在一个函数onload,然后将其绘制在 Canvas 中,从而使我的代码更短?

如何将大量图像放入数组中,然后在函数中使用它。

这是我学习 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();

最佳答案

这可以通过 Promise.all 来完成。我们将为要加载的每个图像做出新的 promise ,并在调用 onload 时解决。一旦 Promise.all 解析,我们就可以调用 initialize 函数并继续执行其余逻辑。这可以避免从 bird.onload 调用主游戏循环的 requestAnimationFrame 的竞争条件,但管道实体等可能尚未加载。

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

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

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

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

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

所有这些都会激励对象聚合游戏实体。我们希望每个实体获得的一些信息可能包括,例如,实体的当前位置、死/活状态、移动和渲染它的功能等。

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

显然,这可能会变成一个重要的重构,但当游戏变得越来越小时,这是必要的一步(我们可以逐步采用这些设计思想)。一般来说,预先咬紧牙关是个好主意。

这是一个概念验证,说明了上面的一些想法。希望这能为您如何管理游戏状态和逻辑提供一些想法。

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

关于javascript - 在一个函数中加载图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58527416/

相关文章:

javascript - 如何在单击按钮时展开下一部分?

html - 使用 W3C 有效徽章有什么意义?

html - Twitter Bootstrap 3.0 - 居中搜索框

java - 如何将 void 方法结果存储到 int 数组方法中

Java读取文本文件中的不同数组

javascript - 当前正在为公会袭击创建 'check-in' 命令

javascript - 做一个 while 循环猜谜游戏

php - 获取整个 div 及其元素并使用 php 邮件功能发送它

javascript - 是否存在替换方法无法从字符串中删除空格的情况?

php - 未定义索引