【问题标题】:Images onload in one function在一个功能中加载图像
【发布时间】:2020-02-19 23:17:09
【问题描述】:

我正在尝试学习 JavaScript,制作我的第一个游戏。如何将所有图像 onload 合并到一个 function 中,然后将其绘制在画布中以缩短我的代码?

如何将大量图像放入数组中,然后将其放入函数中。

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

【问题讨论】:

    标签: javascript html arrays canvas onload-event


    【解决方案1】:

    这可以通过Promise.all 完成。我们将为要加载的每个图像做出新的承诺,在调用 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] 那样访问它,保留各个变量的语义,但让我们能够遍历对象并调用每个实体的@987654333以@函数为例。

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

    拥有某种包含所有初始游戏状态的单独原始数据对象也是一个好主意(这通常是一个外部 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))
    ;

    【讨论】:

    • 非常感谢,第二个例子效果非常棒。
    • 没问题。如果它解决了问题,请随时接受答案。如果这太抽象而无法适用于您的问题,请编辑您的帖子以包含可运行的 sn-p(您可以使用占位符图像),我会更新得更具体一些。祝你好运!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多