双缓冲实际上已经在主流浏览器上被激活。您需要做的是与屏幕同步,这就是提供 requestAnimationFrame :
https ://developer.mozilla.org/en-US/docs/Web/API/window.requestAnimationFrame
这是 requestAnimationFrame 的 polyfill:
requestAnimationFrame (rAF) 还不是标准的:每个“供应商”,即浏览器(Chrome、FF、Safari,...)都有自己的命名。
polyfill 是一段代码,它将在您的环境中“安装”一个功能,无论供应商如何。在这里,您将能够使用 window.requestAnimationFrame 访问 requestAnimationFrame,您将不再关心供应商。
|| 运算符充当“扫描”:它将在第一个“真实”(== 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 将在下次 display 可用时被调用(通常每秒 60 次)。您可以使用 Date.now() 了解当前时间。
有关游戏循环、rAF 和一些时间处理问题的更多见解,请参阅我上面的链接。
这是经过一些重构后的代码:
(小提琴在这里:http: //jsbin.com/uvACEV/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();