web-dev-qa-db-ja.com

HTML5 / Canvasはダブルバッファリングをサポートしていますか?

私がやりたいのは、バッファにグラフィックを描画し、それをそのままキャンバスにコピーできるようにして、アニメーションを実行し、ちらつきを防ぐことです。しかし、私はこのオプションを見つけることができませんでした。誰も私がこれについてどうやって行くことができるか知っていますか?

79
foreyez

次の役立つリンクは、ダブルバッファリングを使用する例と利点を示すことに加えて、html5キャンバス要素を使用するためのパフォーマンスのヒントをいくつか示しています。 jsPerfテストへのリンクが含まれており、ブラウザー全体のテスト結果をBrowserscopeデータベースに集約します。これにより、パフォーマンスのヒントが検証されます。

http://www.html5rocks.com/en/tutorials/canvas/performance/

便宜上、この記事で説明されている効果的なダブルバッファリングの最小限の例を含めました。

// canvas element in DOM
var canvas1 = document.getElementById('canvas1');
var context1 = canvas1.getContext('2d');

// buffer canvas
var canvas2 = document.createElement('canvas');
canvas2.width = 150;
canvas2.height = 150;
var context2 = canvas2.getContext('2d');

// create something on the canvas
context2.beginPath();
context2.moveTo(10,10);
context2.lineTo(10,30);
context2.stroke();

//render the buffered canvas onto the original canvas element
context1.drawImage(canvas2, 0, 0);
32
ricksuggs

非常に簡単な方法は、同じ画面位置に2つのキャンバス要素を配置し、表示する必要があるバッファの可視性を設定することです。非表示に描画し、完了したら反転します。

いくつかのコード:

CSS:

canvas { border: 2px solid #000; position:absolute; top:0;left:0; 
visibility: hidden; }

JSでの反転:

Buffers[1-DrawingBuffer].style.visibility='hidden';
Buffers[DrawingBuffer].style.visibility='visible';

DrawingBuffer=1-DrawingBuffer;

このコードでは、配列 'Buffers []'は両方のキャンバスオブジェクトを保持しています。したがって、描画を開始する場合は、コンテキストを取得する必要があります。

var context = Buffers[DrawingBuffer].getContext('2d');
83

私がテストしたすべてのブラウザは、フレームを描画するコードが完了するまでキャンバスを再描画しないことで、このバッファリングを処理します。 WHATWGメーリングリストも参照してください。 http://www.mail-archive.com/[email protected]/msg19969.html

19
Edward Coffey

いつでもvar canvas2 = document.createElement("canvas");を実行でき、DOMにまったく追加できません。

皆さんがdisplay:none;それは私にとってよりクリーンに見え、単に不器用に見えないキャンバスを持っているよりも、より正確にダブルバッファリングの方法を模倣しています。

13
DeadlyBacon

2年以上後:

ダブルバッファリングを「手動で」実装する必要はありません。 Geary氏は彼の本でこれについて書いています "HTML5 Canvas"

フリッカーを効果的に減らすには、 requestAnimationFrame()

8
ohager

不信者のために、ここにいくつかのちらつきコードがあります。前のサークルを消去するために明示的にクリアしていることに注意してください。

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");

function draw_ball(ball) {
    ctx.clearRect(0, 0, 400, 400);
    ctx.fillStyle = "#FF0000";
    ctx.beginPath();
    ctx.arc(ball.x, ball.y, 30, 0, Math.PI * 2, true);
    ctx.closePath();
    ctx.fill();
}

var deltat = 1;
var ball = {};
ball.y = 0;
ball.x = 200;
ball.vy = 0;
ball.vx = 10;
ball.ay = 9.8;
ball.ax = 0.1;

function compute_position() {
    if (ball.y > 370 && ball.vy > 0) {
        ball.vy = -ball.vy * 84 / 86;
    }
    if (ball.x < 30) {
        ball.vx = -ball.vx;
        ball.ax = -ball.ax;
    } else if (ball.x > 370) {
        ball.vx = -ball.vx;
        ball.ax = -ball.ax;
    }
    ball.ax = ball.ax / 2;
    ball.vx = ball.vx * 185 / 186;
    ball.y = ball.y + ball.vy * deltat + ball.ay * deltat * deltat / 2
    ball.x = ball.x + ball.vx * deltat + ball.ax * deltat * deltat / 2
    ball.vy = ball.vy + ball.ay * deltat
    ball.vx = ball.vx + ball.ax * deltat
    draw_ball(ball);
}

setInterval(compute_position, 40);
<!DOCTYPE html>
<html>
<head><title>Basketball</title></head>
<body>
<canvas id="myCanvas" width="400" height="400" style="border:1px solid #c3c3c3;">
Your browser does not support the canvas element.
</canvas>
</body></html>
6
John

Joshは(しばらく前に)ちらつきを避けるために、ブラウザが「描画プロセスが終了したとき」をどのように認識するかについて尋ねました。私は彼の投稿に直接コメントしたでしょうが、私の担当者は十分に高くありません。また、これは私の意見です。私はそれをバックアップする事実を持っていませんが、私はそれについてかなり自信を持っていると感じ、将来これを読んでいる他の人に役立つかもしれません。

描画が完了したときにブラウザが「認識」しないと推測しています。しかし、ほとんどのjavascriptと同様に、ブラウザーへの制御を放棄せずにコードを実行する限り、ブラウザーは基本的にロックされ、UIに更新/応答しません。キャンバスをクリアし、ブラウザへの制御を放棄せずにフレーム全体を描画すると、実際に完了するまでキャンバスを描画しないと思います。

レンダリングが複数のsetTimeout/setInterval/requestAnimationFrame呼び出しにまたがる状況を設定する場合、1回の呼び出しでキャンバスをクリアし、次のいくつかの呼び出しでキャンバスに要素を描画し、5回の呼び出しごとにサイクルを繰り返します(たとえば)キャンバスは呼び出しのたびに更新されるため、ちらつきが表示されることはありません。

とはいえ、それを信頼するかどうかはわかりません。私たちはすでに、実行前にjavascriptがネイティブマシンコードにコンパイルされる段階にあります(少なくとも、ChromeのV8エンジンは私が理解していることからそれを行っています)。ブラウザーがUIとは別のスレッドでjavascriptを実行し、UI要素へのアクセスを同期し、UIにアクセスしていないjavascriptの実行中にUIが更新/応答できるようになるまで、それほど長くはかからなかったとしても驚かないでしょう。それが発生した場合/(もし他のコードをまだ実行している間にイベントハンドラーがキックオフするなど、克服しなければならない多くのハードルがあることを理解しています)、おそらく使用していないキャンバスアニメーションにちらつきが見られますある種のダブルバッファリング。

個人的には、2つのキャンバス要素が互いの上に配置され、各フレームに表示/描画される交互のアイデアが大好きです。かなり邪魔にならず、おそらく数行のコードで既存のアプリケーションに簡単に追加できます。

6
Lee

Webブラウザーにちらつきはありません!彼らはすでにレンダリングにdblバッファリングを使用しています。 Jsエンジンは、表示する前にすべてのレンダリングを行います。また、コンテキストの保存と復元は、キャンバスのコンテンツ自体ではなく、スタック変換マトリックスデータなどのみをスタックします。したがって、dblバッファリングは必要ありません。

5
Luka

独自のロールを作成するのではなく、クリーンでちらつきのないJavaScriptアニメーションを作成するために既存のライブラリを使用することで、おそらく最高の燃費を得ることができます。

これは人気のあるものです: http://processingjs.org

4
a7drew

2つのキャンバスが必要です:(CSS Z-indexとposition:absoluteに注意してください)

<canvas id="layer1" width="760" height="600" style=" position:absolute; top:0;left:0; 
visibility: visible;  z-index: 0; solid #c3c3c3;">
Your browser does not support the canvas element.
</canvas>
<canvas id="layer2" width="760" height="600" style="position:absolute; top:0;left:0; 
visibility: visible;  z-index: 1; solid #c3c3c3;">
Your browser does not support the canvas element.
</canvas>

最初のキャンバスが表示され、2番目のキャンバスが非表示になっていることがわかります。その後、非表示に描画して、非表示のキャンバスを表示します。それが隠されているとき '隠されたキャンバスをクリア

<script type="text/javascript">
var buff=new Array(2);
buff[0]=document.getElementById("layer1");
buff[1]=document.getElementById("layer2");

ctx[0]=buff[0].getContext("2d");
ctx[1]=buff[1].getContext("2d");
var current=0;
// draw the canvas (ctx[ current ]);
buff[1- current ].style.visibility='hidden';
buff[ current ].style.visibility='visible';
ctx[1-current].clearRect(0,0,760,600);
current =1-current;
3
Aviad Gispan

Opera 9.10は非常に遅く、描画プロセスを示しています。ダブルバッファリングを使用しないブラウザを表示する場合は、Opera 9.10 out。

一部の人々は、ブラウザが描画プロセスがいつ終了するかを何らかの形で決定していると示唆しましたが、それがどのように機能するか説明できますか?描画が遅い場合でも、Firefox、Chrome、またはIE9で明らかなちらつきに気づいていません。ブラウザは、描画命令をさらに実行する直前にディスプレイが更新されていることをどのように知るでしょうか?キャンバスの描画命令を実行せずに5ミリ秒以上の間隔が経過した場合、バッファーを安全に交換できると想定しているので、タイミングを計っていると思いますか?

2
Josh

ほとんどの場合、これを行う必要はありません。ブラウザがこれを実装します。 ただし、必ずしも便利ではない!

図面が非常に複雑な場合は、これをまだ実装する必要があります。ほとんどの画面更新レートは約60Hzです。つまり、16msごとに画面が更新されます。ブラウザの更新レートはこの数値に近い場合があります。図形の完了に100ミリ秒かかる場合、未完了の図形が表示されます。したがって、この状況ではダブルバッファリングを実装できます。

テストを行いました:Clear a rect, wait for some time, then fill with some color.時間を10msに設定しても、ちらつきは見られません。しかし、20msに設定すると、ちらつきが発生します。

2
Chien-Wei Huang