通过 canvas.toDataURL 将画布保存为图像会产生黑色矩形

IT技术 javascript canvas html5-canvas webgl pixi.js
2021-03-16 06:36:49

我正在使用 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);
            })
2个回答

我知道这已经在 SO 上至少回答了 5 次,但是......

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

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

它被清除而不是单独留下的原因是浏览器是否实际交换缓冲区或执行其他操作取决于浏览器。例如,如果抗锯齿打开(这是默认设置),那么它实际上不会进行交换。它做了一个“解决”。它将您刚刚绘制的高分辨率缓冲区转换为正常的分辨率抗锯齿副本到显示缓冲区中。

因此,为了使其更加一致,无论浏览器以何种方式执行其默认设置,它都会始终清除您将要绘制到的任何缓冲区。否则,您将不知道它是否有 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); 

在这种情况下,因为您toDataURL在渲染时调用了相同的 javascript 事件,所以无论枯萎与否preserveDrawingBuffer是真还是假,您总是会得到正确的结果

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

$('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(...); 是一个很好的提示,谢谢。无法使用preserveDrawingBuffer 方法(使用Phaser.js 和WebGL)
2021-04-28 06:36:49
这个答案已经有几年了,但仍然是一个很好的提示。我在使用 Firefox (v59.xx) 时遇到下载问题preserveDrawingBuffer: true,这解决了问题。我现在有preserveDrawingBuffer: false并且图像正在正确下载。
2021-04-28 06:36:49

[笔记]

虽然这个答案是公认的一个,请阅读一个由@gman略低于,它含有一种方式做的更好的方法。


您的问题是您正在使用 webGL 上下文,那么您需要preserveDrawingBuffer将 webGL 上下文属性设置为true才能调用toDataURL()方法。

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