Keyboard response can turn out to be surprisingly complicated. Usually you don't want to pay attention to every keydown event that gets fired, because that depends on the user's keyboard and settings; e.g. one event fires initially then after a pause, a rapid stream are fired. There's also an issue of what to do when users holding down two keys at once.
The following code separates the keyboard response from the animation by having a separate update function. Upon a new arrow key being pressed the square initially moves 1 step (set to 5px), then after 100ms moves continuously until the key is released.
(Fiddle)
var directions = [],
lastPressTime, stepped, ti, frameRate = 30,
squareSpeed = 5, stepTime = 100;
function update() {
var x, y, d = directions[directions.length - 1],
square = document.getElementById('square');
if (!d) {
return;
}
if (new Date().getTime() < lastPressTime + stepTime) {
if (stepped) { // already stepped and <100ms has passed
return; // so do nothing
}
else {
stepped = true;
}
}
x = square.offsetLeft;
y = square.offsetTop;
if (d == 'left') {
x -= squareSpeed;
}
else if (d == 'right') {
x += squareSpeed;
}
else if (d == 'up') {
y -= squareSpeed;
}
else if (d == 'down') {
y += squareSpeed;
}
square.style.left = x + 'px';
square.style.top = y + 'px';
}
function checkArrowKeys(e) {
var arrs = [],
key = window.event ? event.keyCode : e.keyCode;
arrs[37] = 'left';
arrs[38] = 'up';
arrs[39] = 'right';
arrs[40] = 'down';
if (arrs[key]) {
return arrs[key];
}
}
document.onkeydown = function(e) {
var d = checkArrowKeys(e);
if (d) {
// Key not already pressed; add it to array
// of directions and step forward
if (directions.indexOf(d) === -1) {
directions.push(d);
lastPressTime = new Date().getTime();
stepped = false;
}
if (!ti) {
ti = setInterval(update, 1000 / frameRate);
}
}
};
document.onkeyup = function(e) {
var d = checkArrowKeys(e),
i;
if (d) {
i = directions.indexOf(d);
// remove this direction from the array of directions
if (i > -1) {
directions =
directions.slice(0, i).concat(directions.slice(i + 1));
}
// if no keys are down then stop updating
if (directions.length === 0) {
clearInterval(ti);
ti = null;
}
}
};