私がやりたいのは、グラフィックスをバッファーに描画し、それをそのままキャンバスにコピーして、アニメーションを実行し、ちらつきを回避できるようにすることです。しかし、私はこのオプションを見つけることができませんでした。どうすればこれを実行できるか知っている人はいますか?
12 に答える
非常に簡単な方法は、同じ画面位置に 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');
次の役立つリンクは、ダブル バッファリングを使用する例と利点を示すだけでなく、html5 キャンバス要素を使用するためのパフォーマンスのヒントをいくつか示しています。これには、ブラウザー全体のテスト結果を Browserscope データベースに集約する jsPerf テストへのリンクが含まれています。これにより、パフォーマンスのヒントが検証されます。
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);
私がテストしたブラウザはすべて、フレームを描画するコードが完了するまでキャンバスを再描画しないことで、このバッファリングを処理します。WHATWG メーリング リストも参照してください: http://www.mail-archive.com/whatwg@lists.whatwg.org/msg19969.html
いつでも行うことができ
var canvas2 = document.createElement("canvas");
、DOM にまったく追加しません。
皆さんはそれに夢中になっているように見えるのでdisplay:none;
、私にはすっきりしているように見え、ぎこちなく見えないキャンバスを持つよりも正確にダブルバッファリングのアイデアを模倣しています.
信じない人のために、ちらつきのあるコードをいくつか示します。前の円を消去するために明示的にクリアしていることに注意してください。
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>
Josh は (しばらく前に) ちらつきを避けるために、ブラウザが「描画プロセスがいつ終了するか」をどのように認識しているかについて尋ねました。私は彼の投稿に直接コメントしたでしょうが、私の担当者は十分に高くありません. また、これはあくまで私の意見です。私はそれを裏付ける事実を持っていませんが、私はそれについてかなり自信を持っています.
描画がいつ完了したかをブラウザが「認識」していないと思います。しかし、ほとんどの JavaScript と同様に、コードがブラウザーに制御を渡さずに実行されている限り、ブラウザーは基本的にロックされており、UI に応答しない/更新できない/応答しません。ブラウザに制御を渡さずにキャンバスをクリアしてフレーム全体を描画すると、完了するまで実際にキャンバスが描画されないと思います。
レンダリングが複数の setTimeout/setInterval/requestAnimationFrame 呼び出しにまたがる状況を設定した場合、1 回の呼び出しでキャンバスをクリアし、次の数回の呼び出しでキャンバスに要素を描画し、(たとえば) 5 回の呼び出しごとにサイクルを繰り返します。呼び出しごとにキャンバスが更新されるため、ちらつきが見られることは間違いありません。
そうは言っても、それを信頼できるかどうかはわかりません。私たちはすでに、JavaScript が実行前にネイティブ マシン コードにコンパイルされる段階に達しています (少なくとも、私が理解している Chrome の V8 エンジンはそうしています)。ブラウザーが UI とは別のスレッドで JavaScript を実行し始め、UI 要素へのアクセスを同期して、UI にアクセスしていない JavaScript の実行中に UI が更新/応答できるようになるまでにそれほど時間がかからなかったとしても、私は驚かないでしょう。それが発生した場合 (他のコードを実行している間にイベント ハンドラーが起動するなど、克服しなければならない多くのハードルがあることは理解しています)、使用していないキャンバス アニメーションでちらつきが発生する可能性があります。ある種のダブルバッファリング。
個人的には、2 つのキャンバス要素を重ねて配置し、各フレームに交互に表示/描画するというアイデアが気に入っています。かなり控えめで、おそらく数行のコードで既存のアプリケーションに簡単に追加できます。
Webブラウザでちらつきはありません!彼らはすでにレンダリングに dbl バッファリングを使用しています。js エンジンは、表示する前にすべてのレンダリングを行います。また、コンテキストの保存と復元は、キャンバスのコンテンツ自体ではなく、スタック変換マトリックス データなどのみを保存および復元します。したがって、dbl バッファリングは必要ありません。
独自に作成するよりも、既存のライブラリを使用してクリーンでちらつきのない JavaScript アニメーションを作成することで、おそらく最高のマイレージを得ることができます。
これは人気のあるものです: http://processingjs.org
Opera 9.10 は非常に遅く、描画プロセスが表示されます。ダブル バッファリングを使用しないブラウザを見たい場合は、Opera 9.10 を試してみてください。
何人かの人々は、描画プロセスがいつ終了するかをブラウザーが何らかの形で決定していると示唆していましたが、それがどのように機能するのか説明できますか? 描画が遅い場合でも、Firefox、Chrome、または IE9 で明らかなちらつきに気付かなかったので、それが彼らがしていることのように思えますが、それがどのように達成されるかは私には謎です. さらに描画命令が実行される直前に、ブラウザが表示を更新していることをどのように知るのでしょうか? キャンバス描画命令を実行せずに5ミリ秒以上の間隔が経過した場合、バッファを安全にスワップできると想定しているだけだと思いますか?
ほとんどの場合、これを行う必要はありません。ブラウザが実装してくれます。しかし、常に役立つとは限りません。
描画が非常に複雑な場合でも、これを実装する必要があります。ほとんどの画面更新レートは約 60Hz で、16ms ごとに画面が更新されることを意味します。ブラウザの更新率は、この数値に近い場合があります。シェイプが完成するまでに 100 ミリ秒かかる場合は、未完成のシェイプが表示されます。したがって、この状況でダブルバッファリングを実装できます。
テストを行いました:Clear a rect, wait for some time, then fill with some color.時間を 10ms に設定すると、ちらつきは見られません。しかし、20ms に設定すると、ちらつきが発生します。