为什么我的画布在转换为图像后会变成空白?

时间:2012-09-21 21:10:31

标签: javascript canvas webgl

我正在尝试使用以下代码段将this page上的canvas元素转换为png(例如,在JavaScript控制台中输入):

(function convertCanvasToImage(canvas) {
  var image = new Image();
  image.src = canvas.toDataURL("image/png");
  return image;
})($$('canvas')[0]);

不幸的是,我得到的png是完全空白的。另请注意,在调整页面大小后,原始画布将变为空白。

为什么canvas变成空白?如何将此canvas转换为png?

3 个答案:

答案 0 :(得分:16)

Kevin Reid的preserveDrawingBuffer建议是正确的,但有(通常)更好的选择。 tl; dr是最后的代码。

将渲染网页的最终像素放在一起并将其与渲染WebGL内容进行协调可能会非常昂贵。通常的流程是:

  1. JavaScript向WebGL上下文发出绘制命令
  2. JavaScript返回,将控制权返回给主浏览器事件循环
  3. WebGL上下文将绘图缓冲区(或其内容)转换为合成器,以便集成到当前正在屏幕上呈现的网页
  4. Page,屏幕上显示WebGL内容
  5. 请注意,这与大多数OpenGL应用程序不同。在这些内容中,渲染内容通常直接显示,而不是与页面上的一堆其他内容合成,其中一些实际上可能位于WebGL内容之上并与之混合。

    在步骤3之后,WebGL规范被更改为将绘图缓冲区视为基本上为空。您在devtools中运行的代码将在步骤4之后出现,这就是为什么您得到一个空缓冲区。对规范的这一更改允许在平台上实现大幅性能改进,其中步骤3之后的消隐基本上是硬件中实际发生的情况(如在许多移动GPU中)。如果你想在第3步之后解决这个问题,有时会复制WebGL内容,浏览器必须总是在第3步之前复制绘图缓冲区,这会使你的帧率下降在某些平台上突然出现。

    您可以通过将preserveDrawingBuffer设置为true来强制执行此操作并强制浏览器制作副本并保持图像内容的可访问性。来自规范:

      

    可以通过设置WebGLContextAttributes对象的preserveDrawingBuffer属性来更改此默认行为。如果此标志为true,则应保留绘图缓冲区的内容,直到作者清除或覆盖它们为止。如果此标志为false,则在返回呈现函数后尝试使用此上下文作为源图像执行操作可能会导致未定义的行为。这包括readPixels或toDataURL调用,或使用此上下文作为另一个上下文的texImage2D或drawImage调用的源图像。

    在您提供的示例中,代码只是更改上下文创建行:

    gl = canvas.getContext("experimental-webgl", {preserveDrawingBuffer: true});
    

    请记住,它会强制某些浏览器中的慢速路径和性能受到影响,具体取决于渲染的内容和方式。在大多数桌面浏览器中你都应该没问题,因为这些浏览器实际上并不需要复制,而且这些浏览器构成了绝大多数支持WebGL的浏览器......但仅限于现在。

    然而,还有另一种选择(在规范的下一段中有点混淆)。

    基本上,您在第2步之前自己制作副本:在完成所有绘制调用之后但在您从代码中将控制权返回给浏览器之前。这是当WebGL绘图缓冲区仍然可以访问时,您应该可以轻松访问像素。您使用的是相同的toDataUrlreadPixels来电,这只是时间的重要。

    在这里,您将获得两全其美。你得到了一个绘图缓冲区的副本,但是你不需要在每一帧中付费,即使是你不需要副本的那些(可能是大多数),就像你使用preserveDrawingBuffer一样。设置为true。

    在您提供的示例中,只需将代码添加到drawScene的底部,您就会看到右下方的画布副本:

    function drawScene() {
      ...
    
      var webglImage = (function convertCanvasToImage(canvas) {
        var image = new Image();
        image.src = canvas.toDataURL('image/png');
        return image;
      })(document.querySelectorAll('canvas')[0]);
    
      window.document.body.appendChild(webglImage);
    }
    

答案 1 :(得分:2)

这里有一些尝试。我不知道这些中的任何一个是否必须才能完成这项工作,但它们可能会有所作为。

  • preserveDrawingBuffer: true添加到getContext属性。
  • 尝试使用后来做动画的教程;即反复在画布上画画,而不是仅画一次。

答案 2 :(得分:0)

>>> import temp >>> temp.tempfile() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.7/site-packages/temp/__init__.py", line 15, in tempfile f, path = tempfile.mkstemp() AttributeError: 'function' object has no attribute 'mkstemp' 从缓冲区读取数据。

我们不需要使用toDataURL()

在读取数据之前,我们还需要使用preserveDrawingBuffer: true

最后:

render()