Javascript 通过canvas.toDataURL将画布保存到图像会导致黑色矩形

Javascript 通过canvas.toDataURL将画布保存到图像会导致黑色矩形,javascript,canvas,html5-canvas,webgl,pixi.js,Javascript,Canvas,Html5 Canvas,Webgl,Pixi.js,我正在使用Pixi.js并试图将动画的一帧保存到图像中。canvas.toDataUrl应该可以工作,但我得到的只是一个黑色矩形。见现场示例 我用来提取图像数据并设置图像的代码是: var canvas = $('canvas')[0]; var context = canvas.getContext('2d'); $('button').click(function() { var da

我正在使用Pixi.js并试图将动画的一帧保存到图像中。canvas.toDataUrl应该可以工作,但我得到的只是一个黑色矩形。见现场示例

我用来提取图像数据并设置图像的代码是:

            var canvas = $('canvas')[0];
            var context = canvas.getContext('2d');

            $('button').click(function() {

                var data = renderer.view.toDataURL("image/png", 1);
                //tried var data = canvas.toDataURL();
                $('img').attr('src', data);
            })
[注] 虽然这个答案是公认的,但请务必阅读下面的@gman,它确实包含了一种更好的方法


您的问题是您正在使用webGL上下文,然后需要将webGL上下文的
preserveDrawingBuffer
属性设置为
true
,以便能够调用
toDataURL()
方法


或者,您可以强制pixi使用2D上下文,通过使用

我知道这已经被回答了至少5次了,但是

Kaido提到的方法会起作用,但真正的问题是,当与WebGL一起使用时,画布默认有2个缓冲区。正在绘制到的缓冲区和正在显示的缓冲区

当您开始在WebGL画布中绘图时,只要退出当前事件(例如requestAnimationFrame回调),画布就会标记为交换这两个缓冲区。当浏览器重新绘制页面时,它会进行交换。正在绘制的缓冲区将与正在显示的缓冲区交换。您现在正在绘制到其他缓冲区。缓冲区被清除

它被清除而不是被单独保留的原因是浏览器是否真正交换缓冲区或执行其他操作取决于浏览器。例如,如果启用了“抗锯齿”(这是默认设置),则实际上不会进行交换。它有一个“决心”。它将刚绘制的高分辨率缓冲区转换为普通分辨率抗锯齿副本,并将其转换为显示缓冲区

因此,为了使其更加一致,无论浏览器以何种方式执行其默认设置,它总是清除要绘制到的任何缓冲区。否则,您将不知道它是有1帧旧数据还是有2帧旧数据

设置
preserveDrawingBuffer:true
告诉浏览器“始终复制,从不交换”。在这种情况下,不必清除图形缓冲区,因为图形缓冲区中的内容总是已知的。没有交换

这一切有什么意义?关键是,如果要调用
toDataURL
gl.readPixels
,则需要在同一事件中调用它

例如,您的代码可以像这样工作

var capture = false;

$('button').click(function() {
   capture = true;
});

function render() {

  renderer.render(...);

  if (capture) {
    capture = false;
    var data = renderer.view.toDataURL("image/png", 1);
    $('img').attr('src', data);
  }

  requestAnimationFrame(render);
}
requestAnimationFrame(render); 
在这种情况下,因为您在呈现给它的同一个javascript事件中调用
toDataURL
,所以无论wither与否
preserveDrawingBuffer
为true或false,都会得到正确的结果

如果您正在编写的应用程序不经常呈现,您还可以执行以下操作

$('button').click(function() {
   // render right now
   renderer.render(...);

   // capture immediately
   var data = renderer.view.toDataURL("image/png", 1);
   $('img').attr('src', data);
});
默认情况下,
preserveDrawingBuffer
为false的原因是交换比复制快,因此这允许浏览器以尽可能快的速度运行


另请参见encoderOption属性对“image/png”类型没有任何影响,但这不是您的问题。您确定渲染器视图引用了正确的画布对象吗?当您调用它的
toDataURL
方法时,是否在此画布上绘制了任何内容?是和是。你可以看到一个例子@Yep,抱歉,错过了链接。您的问题是您正在使用webGL上下文,那么您需要将webGL上下文的preserveDrawingBuffer属性设置为true,以便能够调用
toDataURL
,您是正确的。我必须将其更改为var renderer=PIXI.autoDetectRenderer(800600,null,true);它成功了。您应该创建一个答案,我将它标记为//render right now renderer.render(…);这是一个很好的提示,谢谢。无法让preserveDrawingBuffer方法工作(使用Phaser.js和WebGL)。这个答案已经有几年了,但仍然是一个很好的提示。我在Firefox(v59.x.x)中使用
preserveDrawingBuffer:true
时遇到下载问题,这解决了问题。我现在有了
preserveDrawingBuffer:false
,图像下载正确。