Solución:
var pane = $('#pane'),
box = $('#box'),
w = pane.width() - box.width(),
d = {},
x = 3;
function newv(v,a,b) {
var n = parseInt(v, 10) - (d[a] ? x : 0) + (d[b] ? x : 0);
return n < 0 ? 0 : n > w ? w : n;
}
$(window).keydown(function(e) { d[e.which] = true; });
$(window).keyup(function(e) { d[e.which] = false; });
setInterval(function() {
box.css({
left: function(i,v) { return newv(v, 37, 39); },
top: function(i,v) { return newv(v, 38, 40); }
});
}, 20);
#pane {
position: relative;
width: 300px;
height: 300px;
border: 2px solid red;
}
#box {
position: absolute;
top: 140px;
left: 140px;
width: 20px;
height: 20px;
background-color: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="pane">
<div id="box"></div>
</div>
Explicaciones variables:
w
– el valor máximo izquierdo / superior que puede tener la caja (para mantenerse dentro de los límites)
x
– la distancia (en px) que la caja se mueve en cada intervalo
d
– este objeto almacena la información sobre qué tecla se está presionando. Por ejemplo, mientras el usuario mantiene presionada la tecla FLECHA IZQUIERDA, d['37']
es true
. De lo contrario es false
. POR CIERTO, 37
es el código de tecla para la tecla FLECHA IZQUIERDA y este valor se almacena en el e.which
propiedad del objeto de evento. los d
el objeto se actualiza en cada keydown
y keyup
evento.
Un setInterval que se ejecuta cada 20 ms, actualiza las propiedades CSS superior e izquierda del elemento de cuadro. Los nuevos valores se calculan mediante el newv
función.
los newv
La función calculará el nuevo valor izquierdo / superior basado en a) el valor anterior v
yb) el d
objeto.
La expresion n < 0 ? 0 : n > w ? w : n
asegura que el nuevo valor está dentro de los límites permitidos (que son 0 to w
). Si n es <0, se devolverá cero. Si n es> w, se devolverá w.
Demostración en vivo: http://jsfiddle.net/simevidas/bDMnX/1299/
Actualizar: Este código tiene la misma funcionalidad que el código original anterior. La única diferencia es que usé nombres más significativos para mis variables y argumentos. Como puede ver, se ve horrible: la versión original es claramente mejor. :P
var pane = $('#pane'),
box = $('#box'),
maxValue = pane.width() - box.width(),
keysPressed = {},
distancePerIteration = 3;
function calculateNewValue(oldValue, keyCode1, keyCode2) {
var newValue = parseInt(oldValue, 10)
- (keysPressed[keyCode1] ? distancePerIteration : 0)
+ (keysPressed[keyCode2] ? distancePerIteration : 0);
return newValue < 0 ? 0 : newValue > maxValue ? maxValue : newValue;
}
$(window).keydown(function(event) { keysPressed[event.which] = true; });
$(window).keyup(function(event) { keysPressed[event.which] = false; });
setInterval(function() {
box.css({
left: function(index ,oldValue) {
return calculateNewValue(oldValue, 37, 39);
},
top: function(index, oldValue) {
return calculateNewValue(oldValue, 38, 40);
}
});
}, 20);
@ Šime Vidas: Su primera solución es simplemente maravillosa. (creo que el segundo es redundante =)
¿Puedo sugerir hacer dos funciones diferentes para el ancho vertical y horizontal? Porque es muy poco probable que tengas que moverte alrededor de un div dentro de un cuadrado perfecto y creo que sería mejor tener algo como esto:
$(function () {
var pane = $('#pane'),
box = $('#box'),
wh = pane.width() - box.width(),
wv = pane.height() - box.height(),
d = {},
x = 5;
function newh(v,a,b) {
var n = parseInt(v, 10) - (d[a] ? x : 0) + (d[b] ? x : 0);
return n < 0 ? 0 : n > wh ? wh : n;
}
function newv(v,a,b) {
var n = parseInt(v, 10) - (d[a] ? x : 0) + (d[b] ? x : 0);
return n < 0 ? 0 : n > wv ? wv : n;
}
$(window).keydown(function(e) { d[e.which] = true; });
$(window).keyup(function(e) { d[e.which] = false; });
setInterval(function() {
box.css({
left: function(i,v) { return newh(v, 37, 39); },
top: function(i,v) { return newv(v, 38, 40); }
});
}, 20);
});
Esto habría sido exactamente lo que estaba buscando.
Si tuviera un diseño receptivo basado en valores de%, sería recomendable ajustar su setInterval de esta manera:
setInterval(function() {
box.css({
left: function(i,v) { return newh(v, 37, 39); },
top: function(i,v) { return newv(v, 38, 40); }
});
wh = pane.width() - box.width();
wv = pane.height() - box.height();
}, 20);
si lo hace, ajusta la altura y el ancho de sus paneles y el cuadro aún se detiene en su borde.
Hice un violín de eso aquí http://jsfiddle.net/infidel/JkQrR/1/
Muchas gracias.
No puedo ver tu demostración, pero aquí tienes un ejemplo simple de “mover el cuadro 1 px en la dirección de las teclas de flecha”:
CSS:
#wrapper {
background-color: gray;
height:200px;
width: 200px;
position:absolute;
}
#mover {
background-color: white;
border: 1px solid red;
height:20px;
width: 20px;
position:relative;
}
Margen:
<div id="wrapper">
<div id="mover"></div>
</div>
JS (usando jQuery):
$("#wrapper").keydown(function(event) {
var $mover = $("#mover");
//if nothing else will move "mover", then track the
//position instead of recalculating it every time:
// var moverPos = $mover.position();
// var left = moverPos.left;
// var top = moverPos.top;
var addTop = function(diff) {
$mover.css("top", ($mover.position().top + diff) + "px");
//if using tracked position:
// top += diff;
// $mover.css("top", top) + "px");
};
var addLeft = function(diff) {
$mover.css("left", ($mover.position().left + diff) + "px");
//if using tracked position:
// left += diff;
// $mover.css("left", left) + "px");
};
switch(event.keyCode) {
case 37: //left
addLeft(-1); break;
case 38: //up
addTop(-1); break;
case 39: //right
addLeft(1); break;
case 40: //down
addTop(1); break;
}
});
Este es solo un ejemplo, es posible que desee agregar verificación de límites, movimientos más grandes, animación más suave, soporte de teclado numérico o cualquier cantidad de otras cosas, pero debería ayudarlo a comenzar.