user1068455
user1068455

Reputation: 101

Drawing multiple images on one canvas

I have an array that contains all the information about how the image should be drawn. I want to draw 2 images and when I do it, it's only drawing the last image. What can I do?

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];
}

Upvotes: 10

Views: 48596

Answers (7)

Narek Grigoryan
Narek Grigoryan

Reputation: 449

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

Upvotes: 0

Ryan Huang
Ryan Huang

Reputation: 3897

I couldn't explain more specific reasons, but here is the solution I figured out. It works for me.

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>

Upvotes: 21

Rob
Rob

Reputation: 1

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

piece of my js game, i hope it helps cheers!

Upvotes: 0

Brett Caswell
Brett Caswell

Reputation: 730

here's a solution, and a recommended alternative; both use <function>.bind(<this>[,<arg1>][,<arg2>])

bind wraps the intended function, and performs a call on it using additional specified parameters (args). The first parameter of bind will be the this instance of the function. Setting it to undefined will pass the original this instance.

anonymous function.

  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];
  }

I prefer not to use anonymous functions for common event handling though; instead, I would declare an object to store handlers.

That way you can replace the handler at runtime. In a manner like the following.

declare object to store event handling functions

  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: Example Fiddle

Upvotes: 0

wutzebaer
wutzebaer

Reputation: 14875

i had similar problems, i like the solution from

https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Basic_animations

just call window.requestAnimationFrame(draw); from your draw function, it will run an infinit loop and draw your images as soon as they are ready

Upvotes: 1

Garrett Vlieger
Garrett Vlieger

Reputation: 9494

If you're loading multiple images, it's recommended that you pre-load all of the images first. See here for more information:

http://www.html5canvastutorials.com/tutorials/html5-canvas-image-loader/

Upvotes: 4

Jeffrey Sweeney
Jeffrey Sweeney

Reputation: 6124

I'm not positive, but that imageObj may not be the imageObj you'd expect in the imageObj.onload function. Instead of doing

 context.drawImage(imageObj, sourceX, sourceY,

See what happens when you do

 context.drawImage(this, sourceX, sourceY,

Upvotes: 5

Related Questions