jaunt
jaunt

Reputation: 5088

A function object can't draw images to an image from a canvas

The title is poorly worded but there's no way to concisely describe the problem. I'm drawing images to the 2D context of a dynamically created canvas. The data of the context is then saved to an image via toDataURL, which is then draw on every frame. The code below is condensed, I will be drawing multiple images to the context, not just one; which is why I'm saving to an image. I will only draw part of the image at once but the image remains constant so I thought this was the best method, if there are alternatives I will happily accept those as answers.

In short: many images drawn on an image. Part of the image draw each frame. The code below works:

var picture = new Image();
picture.src = "images/tilesheet.png";
var canvas = document.getElementById("background");
var context = canvas.getContext("2d");
function generate(){
	var ctx = document.createElement("canvas").getContext("2d");
		ctx.canvas.width = canvas.width;
		ctx.canvas.height = canvas.height;
	ctx.fillStyle = "red";	
	ctx.rect (0, 0, 40, 40);
	ctx.fill();
	ctx.drawImage(picture,0,0);
	image = new Image();
	image.src = ctx.canvas.toDataURL("image/png");		
}
function draw(){
	context.clearRect(0, 0, canvas.width, canvas.height);
	context.drawImage(image, 0,0,100,100,0,0,100,100);	
}
function play(){
  generate();
  setInterval(function(){draw();}, 0.0333);
}
window.onload = function(){
	if(picture.complete)play();
	else picture.onload = play;	
}
<canvas id="background"></canvas>
However this doesn't:
window.Game = {};
canvas = document.getElementById("background");
var tilesheet = new Image();
tilesheet.src = "images/tilesheet.png";
(function(){
	function Map(){
		this.width = 2736;
		this.height = 2736;
		this.image = null;
	}
	Map.prototype.generate = function(){
		var ctx = document.createElement("canvas").getContext("2d");
			ctx.canvas.width = this.width;
			ctx.canvas.height = this.height;
		ctx.fillStyle = "red";	
		ctx.rect (0, 0, 40, 40);
		ctx.fill();
		ctx.drawImage(tilesheet,0,0);
		this.image = new Image();
		this.image.setAttribute('crossOrigin', 'anonymous');
		this.image.src = ctx.canvas.toDataURL("image/png");					
	}
	Map.prototype.draw = function(context){													
		context.drawImage(this.image, 0,0, context.canvas.height, context.canvas.height, 0, 0, context.canvas.height, context.canvas.height);			
	}
	Game.Map = Map;
})();
(function(){
	var room = new Game.Map();
	room.generate();
	var draw = function(){
		canvas.getContext("2d").clearRect(0, 0, canvas.width, canvas.height);	
		room.draw(canvas.getContext("2d"));		
	}
	Game.play = function(){setInterval(function(){draw();}, 0.0333);}
})();
window.onload = function(){
	if(tilesheet.complete)Game.play();
	else tilesheet.onload = Game.play;	
}
<canvas id="background"></canvas>

It seems therefore, that the problem is lying in the fact that I'm using function objects but I'm not sure. What am I doing wrong? There are no errors in the debug console.

Upvotes: 1

Views: 578

Answers (1)

Radosław Miernik
Radosław Miernik

Reputation: 4094

Those two have different execution order.

First one:

  1. ...
  2. Attach image.onload
    1. Call generate()
    2. ...

Second one:

  1. ...
  2. Call generate()
  3. Attach image.onload
    1. ...

That's why it's not working - generate() expects image to be loaded, however second case order doesn't guarantees it.

Instead of

...
room.generate();
...
Game.play = function(){setInterval(function(){draw();}, 0.0333);}

do this

...
Game.play = function(){
    room.generate();
    setInterval(function(){draw();}, 0.0333);
}

Upvotes: 1

Related Questions