【问题标题】:Saving canvas to image via canvas.toDataURL results in black rectangle通过 canvas.toDataURL 将画布保存到图像会导致黑色矩形
【发布时间】:2015-12-09 23:46:37
【问题描述】:

我正在使用 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 方法时,此画布上是否绘制了任何东西?
  • 是的,是的。你可以看一个例子@anatoliyg.github.io/toaster
  • 是的,抱歉,错过了链接。您的问题是您正在使用 webGL 上下文,那么您需要将 webGL 上下文的 preserveDrawingBuffer 属性设置为 true 才能调用toDataURL
  • 你是对的。我不得不将其更改为 var renderer = PIXI.autoDetectRenderer(800, 600, null, true);它奏效了。您应该创建一个答案,我会将其标记为正确

标签: javascript canvas html5-canvas webgl pixi.js


【解决方案1】:

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

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

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

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

因此,为了使其更加一致,无论浏览器默认采用哪种方式,它总是会清除您将要绘制到的任何缓冲区。否则,您将不知道它是否有 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 是真还是假,您总是会得到正确的结果。

如果您编写的应用程序不是不断呈现,您也可以执行类似的操作

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

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

preserveDrawingBuffer 默认为 false 的原因是因为交换比复制快,所以这允许浏览器尽可能快地运行。

另见this answer for one other detail

【讨论】:

  • // 现在渲染 renderer.render(...);是一个很好的提示,谢谢。无法使用 preserveDrawingBuffer 方法(使用 Phaser.js 和 WebGL)
  • 这个答案已有几年历史了,但仍然是一个很好的提示。我在使用 preserveDrawingBuffer: true 的 Firefox (v59.x.x) 中遇到下载问题,这解决了问题。我现在有preserveDrawingBuffer: false 并且图像正在正确下载。
【解决方案2】:

[注意]

虽然这个答案是公认的答案,但请阅读下面@gman 的the one,它确实包含一种更好的方法。


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

或者,您可以强制 pixi 使用 2D 上下文,方法是使用 CanvasRenderer Class

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-09-08
    • 2018-06-01
    • 2014-02-27
    • 1970-01-01
    • 2019-06-21
    相关资源
    最近更新 更多