実際、ダブル バッファリングは主要なブラウザですでに有効になっています。あなたがする必要があるのは、画面と同期することです。それは requestAnimationFrame を提供するものです
:
requestAnimationFrame のポリフィルは次のとおりです。
requestAnimationFrame (rAF) はまだ標準ではありません。すべての「ベンダー」、つまりブラウザ (Chrome、FF、Safari など) には独自の名前が付いています。
ポリフィルは、ベンダーに関係なく、環境に関数を「インストール」するコードです。ここで、window.requestAnimationFrame を使用して requestAnimationFrame にアクセスできるようになり、ベンダーを気にする必要がなくなります。
|| operator は 'scan' として機能します: 最初の 'true' ( == true) 式で停止し、残りの式を評価せずにそれを返します。たとえば、msRequestAnimationFrame が定義されている場合、最後の関数定義は評価されません。
requestAnimationFrame が見つからない場合のフォールバックがひどいことを追加する必要があります: setTimeout の精度は非常に低いですが、ありがたいことに rAF が見つかります: Chrome (接頭辞なし)、Safari (Webkit)、IE >9 ( ms)、Firefox (moz)、Opera (o)。)
// requestAnimationFrame polyfill
var w=window, foundRequestAnimationFrame = w.requestAnimationFrame ||
w.webkitRequestAnimationFrame || w.msRequestAnimationFrame ||
w.mozRequestAnimationFrame || w.oRequestAnimationFrame ||
function(cb) { setTimeout(cb,1000/60); } ;
window.requestAnimationFrame = foundRequestAnimationFrame ;
更新とレンダリングを交換した後 (より良い方法)、実行ループは次のようになります。
function run() {
render();
update();
window.requestAnimationFrame(run);
}
run();
いずれにせよ、あなたが興味を持っている場合は、ここでJavaScriptゲームループについて説明しました:
http://gamealchemist.wordpress.com/2013/03/16/thoughts-on-the-javascript-game-loop/
なぜタイマーが必要なのですか?run は run の遅延呼び出しによって終了するため、上記のコードは run を何度も呼び出します: run は、次に表示が利用可能になったときに呼び出されます (通常は 1 秒あたり 60 回)。そして、Date.now() で現在時刻を知ることができます。
ゲーム ループ、rAF、および処理の問題に関する詳細については、上記のリンクを参照してください。
リファクタリング後のコードは次のとおりです。
(フィドルはこちら: http://jsbin.com/uvACEVA/2/edit )
var canvasWidth = 800, canvasHeight = 600;
var canvas = document.getElementById('canvas');
canvas.width = canvasWidth; canvas.height = canvasHeight;
var ctx = canvas.getContext('2d');
// player definition
var mySprite = {
x: 200,
y: 200,
xvel : 0,
yvel : 0,
velIncr : 0.5,
velAttenuation : 0.95,
maxVel : 15,
width: 50,
height: 50,
color: '#c00',
moved : false
};
mySprite.update = function (dt) {
if (keysDown[37]) {
this.moved = true;
if(this.xvel > -this.maxVel){
this.xvel -= this.velIncr;
}
}
if (keysDown[38]) {
this.moved = true;
if(this.yvel > -this.maxVel){
this.yvel -= this.velIncr;
}
}
if (keysDown[39]) {
this.moved = true;
if(this.xvel < this.maxVel){
this.xvel += this.velIncr;
}
}
if (keysDown[40]) {
this.moved = true;
if(this.yvel < this.maxVel){
this.yvel += this.velIncr;
}
}
// to have a frame-rate independant game,
// compute the real dt in run() and ...
this.x += this.xvel; // ... use this.x += this.xvel * dt;
this.y += this.yvel; // ... this.y += this.yvel * dt;
if(this.moved == false){
this.xvel *= this.velAttenuation;
this.yvel *= this.velAttenuation;
}
this.moved = false;
};
mySprite.draw = function(ctx) {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.width, this.height);
};
// Keyboard handling
var keysDown = [];
window.addEventListener('keydown', function(e) {
keysDown[e.keyCode] = true;
e.preventDefault();
e.stopPropagation();
});
window.addEventListener('keyup', function(e) {
keysDown[e.keyCode] = false;
e.preventDefault();
e.stopPropagation();
});
// requestAnimationFrame polyfill
var w=window, foundRequestAnimationFrame = w.requestAnimationFrame ||
w.webkitRequestAnimationFrame || w.msRequestAnimationFrame ||
w.mozRequestAnimationFrame || w.oRequestAnimationFrame ||
function(cb) { setTimeout(cb,1000/60); } ;
window.requestAnimationFrame = foundRequestAnimationFrame ;
// main animation loop
function update(dt) {
mySprite.update(dt); // only one for the moment.
}
function render(ctx) {
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
mySprite.draw(ctx);
}
var dt = 16; // milliseconds elapsed since last call.
// ideally you should compute it in run(), i didn't for simplicity
function run() {
render(ctx);
update(dt);
window.requestAnimationFrame(run);
}
run();