javascript Canvas toDataURL() 返回空白图像

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26783586/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 06:28:37  来源:igfitidea点击:

Canvas toDataURL() returns blank image

javascriptfirefoxcanvaswebgltodataurl

提问by Ionel Lupu

I'm using glfx.jsto edit my image but when I'm trying to get that image's data using the toDataURL()function I get a blank image (width the same size as the original image).

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

The strange thing is that in Chrome the script works perfect.

奇怪的是,在 Chrome 中,脚本运行完美。

What I want to mention is that the image is loaded in canvasusing the onload event:

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

           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);

            }

Also my image's path is on the same domain;

我的图像路径也在同一个域中;

The problem (in Firefox) is when i hit the save button. Chrome returns the expected result but Firefox return this:

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

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

What could cause this result and how can I fix it?

什么可能导致这种结果,我该如何解决?

回答by gman

Most likely there's some async event between the time you draw to the canvas and the time you call toDataURL. By default the canvas is cleared after every composite. Either prevent the canvas from being cleared by creating the WebGL context with preserveDrawingBuffer: trueas in

很可能在您绘制到画布的时间和您调用toDataURL. 默认情况下,每次合成后都会清除画布。通过使用preserveDrawingBuffer: trueas in创建 WebGL 上下文来防止画布被清除

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

or make sure toDataURL is called before exiting whatever event you're using to render. For example if you do this

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

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

And somewhere else do this

在其他地方这样做

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

Those 2 events, the animation frame, and the clickare not in sync and the canvas may be cleared between calling them. Note: The canvas won't appear cleared as it's double buffered but the buffer toDataURL and other commands that effect that buffer are looking at is cleared.

这 2 个事件 theanimation frame和 theclick不同步,并且在调用它们之间可能会清除画布。注意:画布不会被清除,因为它是双缓冲的,但缓冲区 toDataURL 和影响该缓冲区正在查看的其他命令已被清除。

The solution is either use preserveDrawingBufferor make your call to toDataURLinside the same event as rendering. For example

解决方案是使用preserveDrawingBuffer或调用toDataURL与渲染相同的事件。例如

var captureFrame = false;

function render() {
  drawScene(); 

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

  requestAnimationFrame(render);
}
render();

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

What's the point of preserveDrawingBuffer: falsewhich is the default? It can be significantly faster, especially on mobile to not have to preserve the drawing buffer. Another way to look at it is the browser needs 2 copies of your canvas. The one you're drawing to and the one it's displaying. It has 2 ways to deal with these 2 buffers. (A) double buffer. Let you draw to one, display the other, swap the buffers when you're done rendering which is inferred from exiting any event that issued draw commands (B) Copy the contents of the buffer you're drawing to do the buffer that's being displayed. Swapping is much faster than copying. So, swapping is the default. It's up to the browser what actually happens. The only requirement is that if preserveDrawingBufferis falsethat the drawing buffer get cleared after a composite (which is yet another async event and therefore unpredictable) if preserveDrawingBufferis truethen it must copy so that the drawingbuffer's contents is preserved.

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

Note that once a canvas has a context it will always have the same context. So in other words let's say you change the code that initializes the WebGL context but you still want to set preserveDrawingBuffer: true

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

There are at least 2 ways.

至少有2种方式。

find the canvas first, get a context on it

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

since the code later will end up with the same context.

因为稍后的代码将以相同的上下文结束。

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

Because you've already created a context for that canvas whatever script comes after will get the same context.

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

augment getContext

增加 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>

In this case any webgl context created after augmenting the getContextwill have preserveDrawingBufferset to true.

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