在一个画布上绘制多个图片

10

我有一个包含有关如何绘制图像的所有信息的数组。我想要绘制2个图像,但是当我这样做时,它只绘制最后一个图像。我该怎么办?

for(i = 0; i < tiles.length; i++)
{
    var sourceX = tiles[i][5];
    var sourceY = tiles[i][6];
    var sourceWidth = tiles[i][9];
    var sourceHeight = tiles[i][10];
    var destWidth = sourceWidth;
    var destHeight = sourceHeight;
    var destX = tiles[i][7];
    var destY = tiles[i][8];

    var imageObj = new Image();
    imageObj.onload = function()
    {
        context.drawImage(imageObj, sourceX, sourceY, sourceWidth, sourceHeight, destX, destY, destWidth, destHeight);
    };
    imageObj.src = tiles[i][4];
}
7个回答

20

我无法解释更具体的原因,但这是我想出的解决方案。 它对我起作用。

const getContext = () => document.getElementById('my-canvas').getContext('2d');

// It's better to use async image loading.
const loadImage = url => {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.onload = () => resolve(img);
    img.onerror = () => reject(new Error(`load ${url} fail`));
    img.src = url;
  });
};

// Here, I created a function to draw image.
const depict = options => {
  const ctx = getContext();
  // And this is the key to this solution
  // Always remember to make a copy of original object, then it just works :)
  const myOptions = Object.assign({}, options);
  return loadImage(myOptions.uri).then(img => {
    ctx.drawImage(img, myOptions.x, myOptions.y, myOptions.sw, myOptions.sh);
  });
};

const imgs = [
  { uri: 'http://placehold.it/50x50/f00/000?text=R', x: 15, y:  15, sw: 50, sh: 50 },
  { uri: 'http://placehold.it/50x50/ff0/000?text=Y', x: 15, y:  80, sw: 50, sh: 50 },
  { uri: 'http://placehold.it/50x50/0f0/000?text=G', x: 15, y: 145, sw: 50, sh: 50 }
];

imgs.forEach(depict);
#my-canvas { background: #7F7F7F; }
<canvas id="my-canvas" width="80" height="210"></canvas>


5

我不确定,但是在imageObj.onload函数中使用的imageObj可能不是你所期望的那个。所以不要这样做:

 context.drawImage(imageObj, sourceX, sourceY,

看看你做了什么会发生什么

 context.drawImage(this, sourceX, sourceY,

这很可能是答案的一部分,也可能是解决方案。在for循环中声明局部变量是我在这里看到的另一个问题。 - Brett Caswell
正在使用context.drawImage方法的值仍然超出了作用域范围。也就是说,sourceXsourceY等在每个imageObj(或this)实例上都很可能等同于tiles [tiles.length-1] - Brett Caswell

4

1

0

这里有一个解决方案和一个推荐的替代方案;两者都使用<function>.bind(<this>[,<arg1>][,<arg2>])

bind包装了预期的函数,并使用附加指定参数(args)对其进行调用。 bind的第一个参数将是函数的this实例。将其设置为undefined将传递原始的this实例。

匿名函数。

  var settings;

  for(i = 0; i < tiles.length; i++)
  {
      settings = {};
      settings.sourceX = tiles[i][5];
      settings.sourceY = tiles[i][6];
      settings.sourceWidth = tiles[i][9];
      settings.sourceHeight = tiles[i][10];
      settings.destWidth = sourceWidth;
      settings.destHeight = sourceHeight;
      settings.destX = tiles[i][7];
      settings.destY = tiles[i][8];

      settings.imageObj = new Image();
      settings.imageObj.onload = function(args)
      {
         context.drawImage(args.image, args.sourceX, args.sourceY, args.sourceWidth, args.sourceHeight, args.destX, args.destY, args.destWidth, args.destHeight);
      }.bind(undefined, settings);
      settings.imageObj.src = tiles[i][4];
  }

虽然我不喜欢使用匿名函数来处理常见事件,但我更倾向于声明一个对象来存储处理程序。

这样你就可以在运行时替换处理程序。像下面这样的方式。

声明对象以存储事件处理函数

  var Handlers = { 
      "drawImageHandler" : function drawImageHandler(args)
      {
          context.drawImage(args.image, args.sourceX, args.sourceY, args.sourceWidth, args.sourceHeight, args.destX, args.destY, args.destWidth, args.destHeight);
      }
  };

  function DebugInit() 
  {
      var func = Handlers.drawImageHandler;
      Handlers.drawImageHandler = function(func, args) {
         console.log("%o", args);
         func(args);
      }.bind(undefined, func);
  }


  var settings = {};
  DebugInit(); // when called, encapsulates Handlers.drawImageHandler;

  for(i = 0; i < tiles.length; i++)
  {
      settings = {};
      settings.sourceX = tiles[i][5];
      settings.sourceY = tiles[i][6];
      settings.sourceWidth = tiles[i][9];
      settings.sourceHeight = tiles[i][10];
      settings.destWidth = sourceWidth;
      settings.destHeight = sourceHeight;
      settings.destX = tiles[i][7];
      settings.destY = tiles[i][8];
      settings.imageObj = new Image();
      settings.imageObj.onload = Handlers.drawImageHandler.bind(undefined, settings);      
      settings.imageObj.src = tiles[i][4];
  }

jsFiddle: 示例 Fiddle


0
let img = new Image();
  img.crossOrigin = 'Anonymous';
  const createCanvas = async (imageSrc) => {
      img.src = imageSrc;
      await img.decode();
      context.drawImage(img, x, y, sw, sh)    
  }

如果您有新的问题,请通过单击提问按钮来提出。如果它有助于提供上下文,请包含此问题的链接。- 来自审核 - Ethan

0
img[0]=new Image();
img[0].src="imgimg/img"+i+".png";
img[0].onload=function(){
  cntxt.drawImage(this,400,i*100,100,100);
  i++;
  img[i]=new Image(); 
  img[i].src="invimg/img"+i+".png";
  img[i].onload=this.onload;
};

这是我 JavaScript 游戏的一部分,希望能对你有所帮助,加油!


这会创建一个无限循环。 - Thern

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接