通过canvas.toDataURL保存画布为图像结果显示为黑色矩形

4
我正在使用Pixi.js,尝试将动画的一帧保存为图像。canvas.toDataURL应该可以工作,但我得到的只是一个黑色矩形。在此处查看实时示例:here 我用于提取图像数据并设置图像的代码如下:
            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);
            })

encoderOption属性对“image/png”类型没有影响,但这不是你的问题。你确定renderer.view引用了正确的画布对象吗?当你调用它的toDataURL方法时,这个画布上有任何东西被绘制了吗? - Kaiido
是的,您可以在 http://anatoliyg.github.io/toaster/ 上看到示例。 - nuway
1
是的,抱歉,我错过了链接。你的问题在于你正在使用webGL上下文,那么你需要将webGL上下文的preserveDrawingBuffer属性设置为true,以便能够调用toDataURL - Kaiido
你是正确的。我不得不将它更改为var renderer = PIXI.autoDetectRenderer(800, 600, null, true);,然后它就可以工作了。你应该创建一个答案,我会标记它为正确的。 - nuway
2个回答

19

我知道这个问题在SO上已经至少回答过5次,但...

Kaiido提到的方法可以解决问题,但真正的问题是,在使用WebGL时,默认情况下canvas有两个缓冲区。一个缓冲区用于绘制,另一个缓冲区用于显示。

当您开始在WebGL画布上绘制时,一旦退出当前事件(例如requestAnimationFrame回调),canvas就会被标记为交换这两个缓冲区。当浏览器重新绘制页面时,它会执行交换操作。您正在绘制到的缓冲区与正在显示的缓冲区交换位置。现在您正在绘制到另一个缓冲区。该缓冲区被清除。

之所以清除缓冲区而不仅仅留下它是因为实际上浏览器是否交换缓冲区或执行其他操作取决于浏览器。例如,如果打开了抗锯齿(默认情况下打开),则它实际上不执行交换操作。它执行"解析"。它将您刚刚绘制的高分辨率缓冲区转换为正常分辨率的抗锯齿副本,并将其复制到显示缓冲区中。

因此,为了使其更加一致,无论浏览器使用其默认方式的方式是什么,它都会始终清除您要绘制到的任何缓冲区。否则,您将不知道它是否具有1帧旧数据或2帧旧数据。

preserveDrawingBuffer: true设置为告诉浏览器“始终复制,永远不要交换”。在这种情况下,它不必清除绘图缓冲区,因为所在的绘图缓冲区总是已知的,不需要交换。

所有这些的意义是什么?意义在于,如果您想调用toDataURLgl.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并呈现给它,所以无论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 的原因是交换速度比复制速度更快,因此这使得浏览器可以尽可能地快。

另请参见此答案以获取其他细节


现在渲染的 renderer.render(...) 方法是个好提示,谢谢。使用 Phaser.js 和 WebGL 时无法使保留绘图缓冲区方法起作用。 - r8n5n
这个回答已经有几年了,但仍然是一个很好的提示。我在使用preserveDrawingBuffer: true时在Firefox(v59.x.x)中遇到了下载问题,而这个方法解决了问题。现在我把preserveDrawingBuffer: false设置回来了,图片可以正确下载了。 - Alex

0

[注意]

虽然这个答案已经被接受,但请仔细阅读下面 @gman 的这篇文章,它提供了一种更好的解决方法。


你的问题在于你正在使用webGL上下文,因此你需要将webGL上下文的preserveDrawingBuffer属性设置为true,以便能够调用toDataURL()方法。
或者,你可以通过使用CanvasRenderer类来强制pixi使用2D上下文。CanvasRenderer Class

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接