ravibhagw
ravibhagw

Reputation: 1740

How to copy content from hidden canvas to visible canvas?

I am having some difficulty copying a canvas from my buffer canvas to the canvas on my page. Thus far I have built a Render object, a Level object, and I have my main game loop (currently just a launch function).

I am able to write to the buffer canvas in the Render object just fine (if I add a document.body.append() statement the canvas successfully appends to the document with the necessary content) but I cannot copy from the buffer canvas to my main canvas. See below for a snippet of my code:

function Renderer(bufferWidth, bufferHeight) {
    var c=document.createElement('canvas');
    var ctx=c.getContext('2d');
    c.width=bufferWidth;
    c.height=bufferHeight;

    this.getBufferContext = function() { return ctx; };
    this.getBufferElement = function() { return c; };

    this.drawToCanvas = function(canvasCtx) { 
        canvasCtx.drawImage(c,0,0);
    };
}


var c = document.getElementById('mycanvas');
var ctx = c.getContext('2d');

var render = new Renderer(c.width, c.height);   
var level1 = new Level('images/imagequality.png');

level1.Draw(render.getBufferContext());
render.drawToCanvas(ctx);

Note that Renderer is in a separate file and is loaded using the script tags in my HTML page.

As mentioned earlier, the drawToCanvas() function doesn't appear to successfully copy data from one canvas to another. Appending my source canvas confirms that it contains the expected data.

edit: I have listed my level code below.

function Level(mapname) {
    var map=new Image();
    map.src=mapname;

    this.Draw = function(renderer) {
        map.onload = function() { renderer.drawImage(map,0,0); };
    };
}

Upvotes: 4

Views: 4022

Answers (1)

ckozl
ckozl

Reputation: 6761

I have good news, and I have bad news.

The good news is the code you show here works 100% here is the demo: http://jsbin.com/upatij/edit#javascript,html,live

bad news: that means that something inside your Level code is broken as my stub Level code works perfectly in your framework... :-(

stub Level:

function Level() {
  this.Draw = function(xxctx) {
    for (var i = 0; i < 30; i++) {
      xxctx.moveTo(10 + (i * 40 % 300), 10 + (parseInt(i / 6, 10) * 40));
      xxctx.lineTo(40 + (i * 40 % 300), 40 + (parseInt(i / 6, 10) * 40));
      xxctx.moveTo(40 + (i * 40 % 300), 10 + (parseInt(i / 6, 10) * 40));
      xxctx.lineTo(10 + (i * 40 % 300), 40 + (parseInt(i / 6, 10) * 40));
    }
    xxctx.stroke();
  };
}

good luck! -ck

AFTER YOUR SEEING YOUR LEVEL CODE:

The problem is one of synchronicity, your use of classes here are hiding the problem from you, via deceptive naming, as your Level.Draw, is not a draw function at all... let me unwrap it for you:

var c = document.getElementById('mycanvas');
var ctx = c.getContext('2d');

// var render = new Renderer(c.width, c.height);
var Renderer_c = document.createElement('canvas');
var Renderer_ctx = Renderer_c.getContext('2d');
document.body.appendChild(Renderer_c); //added to show
Renderer_c.width = c.width;
Renderer_c.height = c.height;

// var level1 = new Level('images/imagequality.png');
var map = new Image();
document.body.appendChild(map); //add to show
map.src = 'http://th06.deviantart.net/fs71/150/i/2011/255/9/5/omnom_upside_down_by_earnurm-d49pjnl.png';

console.log('at ' + 1);
// level1.Draw(render.getBufferContext());
map.onload = function() { 
  console.log('at ' + 3);
  //this happens async:
  alert('drawing now!');
  Renderer_ctx.drawImage(map,0,0); 
};

console.log('at ' + 2);
// render.drawToCanvas(ctx);
ctx.drawImage(Renderer_c, 0, 0);

If you run that code you will see that at the moment at which onload is called everything else has already executed, you'll notice how the console will read:

at 1 
at 2
at 3

and as such, at the moment when alert('drawing now!'); is executed...

// render.drawToCanvas(ctx);
ctx.drawImage(Renderer_c, 0, 0);

will have already run... Basically your Draw() function is actually an asynchronous "Load". Unfortunately, you current conceptualization does not work. Your Draw() function needs to be an async one like this:

function Level(mapname) {
    var map=new Image();
    document.body.appendChild(map); //add to show
    map.src=mapname;

    this.asyncDraw = function(renderer, onComplete) {
        map.onload = function() { 
          renderer.drawImage(map,0,0); 
          onComplete();
        };
    };
}

and the function should then be called like this in your example:

level1.asyncDraw(render.getBufferContext(), function() {
  render.drawToCanvas(ctx);
});

I should probably go on to say that this type of asynchronisity makes HTML5 game programming a little tricky as you really have to throw up the "Loading..." spinner and refrain from going into your rendering loop until all "resources" have loaded. In all practicality you need the concept of "ready" eg. Load(fOnReady) AND Draw(ctx) instead of just asyncDraw(ctx, fOnReady)...

the updated jsbin is here: http://jsbin.com/upatij/2/edit

hope this helps -ck

Upvotes: 5

Related Questions