Canvas toDataURL()仅在Firefox中返回空白图像

boy*_*oyd 17 javascript firefox canvas webgl todataurl

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

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

我想提到的是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);

            }
Run Code Online (Sandbox Code Playgroud)

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

问题(在Firefox中)是我点击保存按钮.Chrome会返回预期的结果,但Firefox会返回此信息:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA7YAAAIWCAYAAABjkRHCAAAHxklEQVR4nO3BMQEAAADCoPVPbQZ/oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
... [ lots of A s ] ... 
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAzwD6aAABkwvPRgAAAABJRU5ErkJggg==
Run Code Online (Sandbox Code Playgroud)

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

gma*_*man 22

很可能在您绘制画布的时间和调用的时间之间存在一些异步事件toDataURL.默认情况下,画布在每个复合后清除.无论是防止画布由与创建WebGL的情况下被清除preserveDrawingBuffer: true,如

var gl = canvas.getContext("webgl", {preserveDrawingBuffer: true});
Run Code Online (Sandbox Code Playgroud)

或者确保在退出用于渲染的任何事件之前调用toDataURL.例如,如果你这样做

function render() {
  drawScene(); 
  requestAnimationFrame(render);
}
render();
Run Code Online (Sandbox Code Playgroud)

而其他地方也这样做

someElement.addEventListener('click', function() {
  var data = someCanvas.toDataURL();
}, false);
Run Code Online (Sandbox Code Playgroud)

这两个事件,animation frame和,click不同步,画布可以在调用它们之间清除.注意:画布不会被清除,因为它是双缓冲的,但缓冲区toDataURL和其他影响该缓冲区的命令被清除.

解决方案是使用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);
Run Code Online (Sandbox Code Playgroud)

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

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

至少有两种方式.

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

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

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

因为您已经为该画布创建了一个上下文,无论后面的脚本获得相同的上下文.

增加 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>
Run Code Online (Sandbox Code Playgroud)

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