19

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

            }

また、私のイメージのパスは同じドメインにあります。

問題 (Firefox の場合) は、保存ボタンを押したときです。Chrome は期待どおりの結果を返しますが、Firefox は次のように返します。

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

この結果の原因は何ですか?どうすれば修正できますか?

4

1 に答える 1

29

ほとんどの場合、キャンバスに描画してから呼び出すまでに非同期イベントが発生しますtoDataURL。デフォルトでは、合成のたびにキャンバスがクリアされます。次のように WebGL コンテキストを作成して、キャンバスがクリアされないようにしますpreserveDrawingBuffer: true

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

または、レンダリングに使用しているイベントを終了する前に toDataURL が呼び出されていることを確認してください。たとえば、これを行うと

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

そして、他の場所でこれを行います

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

これらの 2 つのイベント、animation frame、および はclick同期されておらず、それらを呼び出す間にキャンバスがクリアされる場合があります。注: キャンバスはダブル バッファリングされているため、クリアされたようには見えませんが、バッファ toDataURL およびそのバッファが参照している他のコマンドはクリアされます。

解決策は、レンダリングと同じイベント内で使用する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);

preserveDrawingBuffer: falseデフォルトのポイントは何ですか?特にモバイルでは、描画バッファを保持する必要がないため、大幅に高速化できます。別の見方をすると、ブラウザにはキャンバスのコピーが 2 つ必要です。あなたが描いているものとそれが表示されているもの。これら 2 つのバッファを処理するには 2 つの方法があります。(A) ダブル バッファー。一方に描画し、もう一方を表示し、描画コマンドを発行したイベントを終了することから推測されるレンダリングが完了したらバッファーを交換します (B) 描画しているバッファーの内容をコピーして、表示されているバッファーを実行します. スワップは、コピーよりもはるかに高速です。したがって、スワッピングがデフォルトです。実際に何が起こるかはブラウザ次第です。唯一の要件はpreserveDrawingBufferfalse合成の後に描画バッファがクリアされる (これは別の非同期イベントであり、予測不可能です) 場合preserveDrawingBufferは、描画バッファtrueの内容が保持されるようにコピーする必要があります。

キャンバスがコンテキストを持つと、常に同じコンテキストを持つことに注意してください。つまり、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 コンテキストが truegetContextpreserveDrawingBuffer設定されます。

于 2014-11-06T22:41:46.627 に答える