Canvas toDataURL() 返回空白图像

Canvas toDataURL() returns blank image

提问人:Ionel Lupu 提问时间:11/6/2014 最后编辑:gmanIonel Lupu 更新时间:9/12/2020 访问量:20374

问:

我正在使用 glfx.js 来编辑我的图像,但是当我尝试使用该函数获取该图像的数据时,我得到了一个空白图像(宽度与原始图像大小相同)。toDataURL()

奇怪的是,在Chrome中,脚本运行良好。

我想提到的是,图像是使用 onload 事件加载的:canvas

           img.onload = function(){

                try {
                    canvas = fx.canvas();
                } catch (e) {
                    alert(e);
                    return;
                }

                // convert the image to a texture
                texture = canvas.texture(img);

                // draw and update canvas
                canvas.draw(texture).update();

                // replace the image with the canvas
                img.parentNode.insertBefore(canvas, img);
                img.parentNode.removeChild(img);

            }

此外,我的图像的路径位于同一域上;

问题(在Firefox中)是我点击保存按钮时。Chrome 返回预期的结果,但 Firefox 返回以下结果:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA7YAAAIWCAYAAABjkRHCAAAHxklEQVR4nO3BMQEAAADCoPVPbQZ/oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
... [ lots of A s ] ... 
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAzwD6aAABkwvPRgAAAABJRU5ErkJggg==

什么原因可能导致此结果,我该如何解决?

javascript firefox canvas webgl todataurl

评论

0赞 A1rPun 11/6/2014
您正在编辑的图像是否在同一个域中?只是为了排除显而易见的。
0赞 Ionel Lupu 11/6/2014
是的,它是。我会编辑我的帖子来提及这一点!Firefox(而不是 chrome)中的图像不在同一域中存在问题?
0赞 11/7/2014
似乎是某处发生的异步操作。如果在调用 toDataURL() 时未加载图像,则画布将为空白。
0赞 Ionel Lupu 11/7/2014
图像已加载到画布上,我可以看到并对其进行编辑。问题是 toDataURL 没有从 canvas 中获取正确的信息。
1赞 Patrick 10/5/2018
作为要检查的额外内容,如果画布的宽度或高度为 0,则根据文档,返回 .我在(错误地)将画布的大小设置为 0 后得到了这个。getDataURL()"data:,"

答:

34赞 gman 11/7/2014 #1

最有可能的是,在绘制到画布的时间和调用 .默认情况下,画布在每次合成后都会被清除。要么通过使用toDataURLpreserveDrawingBuffer: true

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

或者确保在退出用于呈现的任何事件之前调用 toDataURL。例如,如果您这样做

function render() {
  drawScene(); 
  requestAnimationFrame(render);
}
render();

在其他地方这样做

someElement.addEventListener('click', function() {
  var data = someCanvas.toDataURL();
}, false);

这两个事件 , 和 不同步,在调用它们之间可能会清除画布。注意:画布不会显示为清除,因为它是双缓冲的,但缓冲区 toDataURL 和影响该缓冲区正在查看的其他命令将被清除。animation frameclick

解决方案是在渲染相同的事件中使用或调用。例如preserveDrawingBuffertoDataURL

var captureFrame = false;

function render() {
  drawScene(); 

  if (captureFrame) {
    captureFrame = false;
    var data = someCanvas.toDataURL();
    ...
  }

  requestAnimationFrame(render);
}
render();

someElement.addEventListener('click', function() {
  captureFrame = true;
}, false);

哪个是默认值有什么意义?它可以明显更快,尤其是在移动设备上,不必保留绘图缓冲区。另一种看待它的方法是浏览器需要 2 个画布副本。您正在绘制的那个和它正在显示的那个。它有 2 种方法来处理这 2 个缓冲区。(一)双缓冲。允许您绘制到一个,显示另一个,在完成渲染后交换缓冲区,这是从退出发出绘制命令的任何事件中推断出来的 (B) 复制要绘制的缓冲区的内容以执行正在显示的缓冲区。交换比复制快得多。因此,交换是默认设置。这取决于浏览器实际发生的情况。唯一的要求是,如果 is 在复合后清除绘图缓冲区(这是另一个异步事件,因此不可预测),如果是,则必须复制,以便保留绘图缓冲区的内容。preserveDrawingBuffer: falsepreserveDrawingBufferfalsepreserveDrawingBuffertrue

请注意,一旦画布具有上下文,它将始终具有相同的上下文。换句话说,假设您更改了初始化 WebGL 上下文的代码,但您仍然希望设置preserveDrawingBuffer: true

至少有 2 种方法。

首先找到画布,获取其上下文

因为稍后的代码最终将具有相同的上下文。

<script>
document.querySelector('#somecanvasid').getContext(
    'webgl', {preserveDrawingBuffer: true});
</script>
<script src="script/that/will/use/somecanvasid.js"></script>

因为你已经为该画布创建了一个上下文,所以后面的任何脚本都将获得相同的上下文。

增加getContext

<script>
HTMLCanvasElement.prototype.getContext = function(origFn) {
  return function(type, attributes) {
    if (type === 'webgl') {
      attributes = Object.assign({}, attributes, {
        preserveDrawingBuffer: true,
      });
    }
    return origFn.call(this, type, attributes);
  };
}(HTMLCanvasElement.prototype.getContext);
</script>
<script src="script/that/will/use/webgl.js"></script>

在这种情况下,在扩充 之后创建的任何 webgl 上下文都将设置为 true。getContextpreserveDrawingBuffer

评论

1赞 Ionel Lupu 11/8/2014
因为我使用的是 glfx.js 库,所以我无法直接访问 canvas 和 render 方法。我调用了 toDataURL 函数事件,超时为 5 秒,但它不起作用。我不认为这里有什么异步的东西......会不会是别的什么?
0赞 gman 11/10/2014
调用超时为 5 秒的 toDataURL 是一个异步事件。您使用的是 JavaScript。您拥有 glfx.js 的源代码。改变它。
0赞 Ionel Lupu 11/10/2014
我设法通过使用 glfx 函数 () 更新 canvas 元素来解决这个问题。然而,我不知道为什么这个问题只出现在 FF 中,而不是在 Chrome 中。谢谢!update()
1赞 gman 11/11/2014
之所以是 FF,只是因为这里的规范模棱两可。WebGL 规范实际上表示,如果向 WebGL 上下文发出绘制命令,则下次网页与页面合成时,画布的绘制缓冲区将被清除。合成发生时是异步的。换句话说,你不知道它什么时候会发生,也不知道在你画东西和合成发生和缓冲区被清除之间会发生什么事件。这意味着它在每个浏览器上都是不同的。在 Chrome 的情况下,您会在清除画布之前获得画布,在清除画布后获得画布。
0赞 Ionel Lupu 11/11/2014
这就解释了。谢谢!