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

nuw*_*way 2 javascript canvas webgl html5-canvas pixi.js

我正在使用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);
            })
Run Code Online (Sandbox Code Playgroud)

gma*_*man 13

我知道至少有5次在SO上回答了但是......

Kaiido提到的内容可行,但真正的问题是,当与WebGL一起使用时,canvas默认有2个缓冲区.您要绘制的缓冲区和正在显示的缓冲区.

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

它被清除而不仅仅是单独留下的原因是浏览器是否实际交换缓冲区或是否由浏览器决定.例如,如果打开了抗锯齿(这是默认值),那么它实际上不会进行交换.它做了"解决".它将您刚刚绘制的highres缓冲区转换为正常的res抗锯齿副本到显示缓冲区.

因此,为了使其更加一致,无论浏览器以何种方式执行默认操作,它始终会清除您要绘制的缓冲区.否则你不知道它是否有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); 
Run Code Online (Sandbox Code Playgroud)

在这种情况下,因为你调用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);
});
Run Code Online (Sandbox Code Playgroud)

preserveDrawingBuffer默认情况下,原因是错误,因为交换比复制更快,因此这允许浏览器尽可能快地进行.

另请参阅此答案以获取其他详细信息


Kai*_*ido 0

[笔记]

虽然这个答案已被接受,但请务必阅读下面 @gman 的答案,它确实包含一种更好的方法。


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

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