如何将隐藏画布中的内容复制到可见画布?

rav*_*agw 4 html javascript canvas

将画布从缓冲区画布复制到页面上的画布时遇到一些困难。到目前为止,我已经构建了一个渲染对象、一个关卡对象,并且我有了主游戏循环(目前只是一个启动函数)。

我可以很好地写入渲染对象中的缓冲区画布(如果我添加 document.body.append() 语句,画布会成功地将必要的内容追加到文档中),但我无法从缓冲区画布复制到我的主要画布。请参阅下面的我的代码片段:

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);
Run Code Online (Sandbox Code Playgroud)

请注意,渲染器位于单独的文件中,并使用 HTML 页面中的脚本标记加载。

如前所述,drawToCanvas() 函数似乎无法成功地将数据从一个画布复制到另一个画布。附加我的源画布确认它包含预期的数据。

编辑:我在下面列出了我的级别代码。

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

    this.Draw = function(renderer) {
        map.onload = function() { renderer.drawImage(map,0,0); };
    };
}
Run Code Online (Sandbox Code Playgroud)

cko*_*ozl 5

我有好消息,也有坏消息。

好消息是您在此处显示的代码 100% 有效,这里是演示: http: //jsbin.com/upatij/edit#javascript,html,live

坏消息:这意味着您的级别代码中的某些内容被破坏,因为我的存根级别代码在您的框架中完美运行...:-(

存根级别:

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();
  };
}
Run Code Online (Sandbox Code Playgroud)

祝你好运!-ck

看到您的等级代码后:

问题是同步性之一,您在这里使用类通过欺骗性命名向您隐藏了问题,因为您的 Level.Draw 根本不是绘图函数...让我为您解开它:

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);
Run Code Online (Sandbox Code Playgroud)

如果您运行该代码,您将看到此时onload调用的所有其他内容都已执行,您将注意到控制台将如何读取:

at 1 
at 2
at 3
Run Code Online (Sandbox Code Playgroud)

因此,在alert('drawing now!');被执行的那一刻......

// render.drawToCanvas(ctx);
ctx.drawImage(Renderer_c, 0, 0);
Run Code Online (Sandbox Code Playgroud)

将已经运行...基本上你的Draw()函数实际上是一个异步“加载”。不幸的是,您当前的概念化不起作用。您的Draw()函数需要是一个异步函数,如下所示:

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();
        };
    };
}
Run Code Online (Sandbox Code Playgroud)

然后在您的示例中应该像这样调用该函数:

level1.asyncDraw(render.getBufferContext(), function() {
  render.drawToCanvas(ctx);
});
Run Code Online (Sandbox Code Playgroud)

我可能应该继续说,这种类型的异步性使得 HTML5 游戏编程有点棘手,因为您确实必须抛出“正在加载...”微调器,并在所有“资源”加载完成之前避免进入渲染循环。在所有实用性中,您需要“准备好”的概念,例如。Load(fOnReady)Draw(ctx)不仅仅是asyncDraw(ctx, fOnReady)...

更新后的 jsbin 在这里: http: //jsbin.com/upatij/2/edit

希望这有帮助-ck