我正在尝试开发JavaScript游戏引擎,我遇到了这个问题:
问题在于,当我向右按,然后按空格键时,角色跳跃然后停止移动。
我使用keydown
函数来按下键。如何检查是否一次按下多个键?
答案 0 :(得分:276)
如果您了解概念
,则可以轻松进行多次击键检测我这样做是这样的:
var map = {}; // You could also use an array
onkeydown = onkeyup = function(e){
e = e || event; // to deal with IE
map[e.keyCode] = e.type == 'keydown';
/* insert conditional here */
}
此代码非常简单:由于计算机一次只传递一次击键,因此会创建一个数组来跟踪多个键。然后可以使用该数组一次检查一个或多个键。
只是为了解释,让我们说你按 A 和 B ,每个都会触发keydown
事件,将map[e.keyCode]
设置为e.type == keydown
的值,其值为 true 或 false 。现在,map[65]
和map[66]
都设置为true
。当您放开A
时,keyup
事件将触发,导致相同的逻辑确定map[65]
(A)的相反结果,现在 false ,但由于map[66]
(B)仍在" down" (它没有触发键盘事件),它仍然是 true 。
map
数组,通过这两个事件,如下所示:
// keydown A
// keydown B
[
65:true,
66:true
]
// keyup A
// keydown B
[
65:false,
66:true
]
你现在可以做两件事:
A)当您想要快速找出一个或多个密钥代码时,可以创建密钥记录器(example)作为参考。假设您已定义了一个html元素并使用变量element
指向它。
element.innerHTML = '';
var i, l = map.length;
for(i = 0; i < l; i ++){
if(map[i]){
element.innerHTML += '<hr>' + i;
}
}
注意:您可以通过id
属性轻松抓取元素。
<div id="element"></div>
这会创建一个html元素,可以使用element
alert(element); // [Object HTMLDivElement]
您甚至不必使用document.getElementById()
或$()
来抓住它。但是为了兼容性,更广泛地推荐使用jQuery&#39; {
只需确保脚本标记位于HTML正文之后。 优化提示:大多数大牌网站都将脚本标记放在主体标记之后进行优化。这是因为脚本标记会阻止其他元素加载,直到脚本完成下载。将其置于内容之前可以预先加载内容。
B(您感兴趣的地方)您可以在$()
的时间检查一个或多个密钥,请举例:
/*insert conditional here*/
修改:这不是最具可读性的代码段。可读性很重要,所以你可以尝试这样的东西让眼睛更容易:
if(map[17] && map[16] && map[65]){ // CTRL+SHIFT+A
alert('Control Shift A');
}else if(map[17] && map[16] && map[66]){ // CTRL+SHIFT+B
alert('Control Shift B');
}else if(map[17] && map[16] && map[67]){ // CTRL+SHIFT+C
alert('Control Shift C');
}
用法:
function test_key(selkey){
var alias = {
"ctrl": 17,
"shift": 16,
"A": 65,
/* ... */
};
return key[selkey] || key[alias[selkey]];
}
function test_keys(){
var keylist = arguments;
for(var i = 0; i < keylist.length; i++)
if(!test_key(keylist[i]))
return false;
return true;
}
这样更好吗?
test_keys(13, 16, 65)
test_keys('ctrl', 'shift', 'A')
test_key(65)
test_key('A')
(编辑结束)
此示例检查 Ctrl Shift A , Ctrl Shift B , Ctrl Shift C
它就像那样简单:)
作为一般规则,最好记录代码,尤其是密钥代码(如if(test_keys('ctrl', 'shift')){
if(test_key('A')){
alert('Control Shift A');
} else if(test_key('B')){
alert('Control Shift B');
} else if(test_key('C')){
alert('Control Shift C');
}
}
),以便您记住它们是什么。
您还应该将密钥代码与文档(// CTRL+ENTER
,而不是CTRL+ENTER => map[17] && map[13]
)放在一起。这样,当您需要返回并编辑代码时,您将不会感到困惑。
如果检查不同数量的组合(例如 Ctrl Shift Alt Enter 和 Ctrl < / kbd> 输入),在较大的组合后放置较小的组合,否则较小的组合将覆盖较大的组合(如果它们足够相似)。例如:
map[13] && map[17]
当处理警报或从主窗口获得焦点的任何事情时,您可能希望在条件完成后包括// Correct:
if(map[17] && map[16] && map[13]){ // CTRL+SHIFT+ENTER
alert('Whoa, mr. power user');
}else if(map[17] && map[13]){ // CTRL+ENTER
alert('You found me');
}else if(map[13]){ // ENTER
alert('You pressed Enter. You win the prize!')
}
// Incorrect:
if(map[17] && map[13]){ // CTRL+ENTER
alert('You found me');
}else if(map[17] && map[16] && map[13]){ // CTRL+SHIFT+ENTER
alert('Whoa, mr. power user');
}else if(map[13]){ // ENTER
alert('You pressed Enter. You win the prize!');
}
// What will go wrong: When trying to do CTRL+SHIFT+ENTER, it will
// detect CTRL+ENTER first, and override CTRL+SHIFT+ENTER.
// Removing the else's is not a proper solution, either
// as it will cause it to alert BOTH "Mr. Power user" AND "You Found Me"
以重置阵列。这是因为某些内容(例如map = []
)会将焦点从主窗口移开,从而导致“键入”按键。事件不要触发。例如:
alert()
这是我发现的令人讨厌的事情,其中包括以下解决方案:
问题:由于浏览器通常对键组合有默认操作(例如 Ctrl D 激活书签窗口,或 Ctrl Shift C 在maxthon上激活skynote),您可能还想在if(map[17] && map[13]){ // CTRL+ENTER
alert('Oh noes, a bug!');
}
// When you Press any key after executing this, it will alert again, even though you
// are clearly NOT pressing CTRL+ENTER
// The fix would look like this:
if(map[17] && map[13]){ // CTRL+ENTER
alert('Take that, bug!');
map = {};
}
// The bug no longer happens since the array is cleared
之后添加return false
,这样您网站的用户就不会感到沮丧&#34;重复文件&#34;功能,放在 Ctrl D 上,改为为该页面添加书签。
map = []
如果没有if(map[17] && map[68]){ // CTRL+D
alert('The bookmark window didn\'t pop up!');
map = {};
return false;
}
,书签窗口会弹出,让用户感到沮丧。
好的,所以你总是不想在那时退出这个功能。这就是return false
功能存在的原因。它的作用是设置一个内部标志,告诉解释器不允许浏览器运行其默认操作。之后,继续执行该功能(而event.preventDefault()
将立即退出该功能)。
在决定是使用return
还是return false
e.preventDefault()
用户SeanVieira在评论中指出event.keyCode
已被弃用。
在那里,他提供了一个很好的选择:event.keyCode
,它返回被按下的键的字符串表示,如{kbd> A 的event.key
或"a"
对于 Shift 。
我继续做了tool来检查所说的琴弦。
"Shift"
vs element.onevent
使用element.addEventListener
注册的处理程序可以堆叠,并按注册顺序进行调用,而直接设置addEventListener
则相当激进并覆盖您之前拥有的任何内容。
.onevent
document.body.onkeydown = function(ev){
// do some stuff
ev.preventDefault(); // cancels default actions
return false; // cancels this function as well as default actions
}
document.body.addEventListener("keydown", function(ev){
// do some stuff
ev.preventDefault() // cancels default actions
return false; // cancels this function only
});
属性似乎覆盖了所有内容,.onevent
和ev.preventDefault()
的行为可能相当难以预测。
在任何一种情况下,通过return false;
注册的处理程序似乎更容易编写和推理。
Internet Explorer的非标准实现也有addEventlistener
,但这已经超出了弃用范围,甚至与JavaScript无关(它与一种叫做 JScript的深奥语言有关) )。尽可能避免使用多语言代码符合您的最佳利益。
为了解决混淆/投诉,我写了一个&#34;类&#34;做这个抽象(pastebin link):
attachEvent("onevent", callback)
这门课不做任何事情,它不会处理每一个可以想象的用例。我不是图书馆的人。但对于一般的交互式使用它应该没问题。
要使用此类,请创建一个实例并将其指向要与键盘输入关联的元素:
function Input(el){
var parent = el,
map = {},
intervals = {};
function ev_kdown(ev)
{
map[ev.key] = true;
ev.preventDefault();
return;
}
function ev_kup(ev)
{
map[ev.key] = false;
ev.preventDefault();
return;
}
function key_down(key)
{
return map[key];
}
function keys_down_array(array)
{
for(var i = 0; i < array.length; i++)
if(!key_down(array[i]))
return false;
return true;
}
function keys_down_arguments()
{
return keys_down_array(Array.from(arguments));
}
function clear()
{
map = {};
}
function watch_loop(keylist, callback)
{
return function(){
if(keys_down_array(keylist))
callback();
}
}
function watch(name, callback)
{
var keylist = Array.from(arguments).splice(2);
intervals[name] = setInterval(watch_loop(keylist, callback), 1000/24);
}
function unwatch(name)
{
clearInterval(intervals[name]);
delete intervals[name];
}
function detach()
{
parent.removeEventListener("keydown", ev_kdown);
parent.removeEventListener("keyup", ev_kup);
}
function attach()
{
parent.addEventListener("keydown", ev_kdown);
parent.addEventListener("keyup", ev_kup);
}
function Input()
{
attach();
return {
key_down: key_down,
keys_down: keys_down_arguments,
watch: watch,
unwatch: unwatch,
clear: clear,
detach: detach
};
}
return Input();
}
这样做会将一个新的输入监听器附加到var input_txt = Input(document.getElementById("txt"));
input_txt.watch("print_5", function(){
txt.value += "FIVE ";
}, "Control", "5");
元素(让我们假设它是一个textarea),并为关键组合#txt
设置一个观察点。 。如果Ctrl+5
和Ctrl
都关闭,则会调用您传入的回调函数(在本例中为将5
添加到textarea的函数)。回调与名称"FIVE "
相关联,因此要删除它,只需使用:
print_5
从input_txt.unwatch("print_5");
元素中分离input_txt
:
txt
这样,垃圾收集可以拾取对象(input_txt.detach();
),如果它被丢弃,你就不会遗留旧的僵尸事件监听器。
为了彻底,这里是对C / Java风格的API类的快速参考,以便您知道它们返回的内容以及它们所期望的参数。
input_txt
如果
Boolean key_down (String key);
关闭,则返回true
,否则返回false。key
如果所有键
Boolean keys_down (String key1, String key2, ...);
都关闭,则返回true
,否则返回false。key1 .. keyN
创建一个&#34;观察点&#34;按下所有
void watch (String name, Function callback, String key1, String key2, ...);
将触发回调keyN
通过名称
删除所述观察点void unwatch (String name);
按下&#34;键&#34;缓存。相当于
void clear (void);
以上map = {}
从父元素中分离
void detach (void);
和ev_kdown
侦听器,从而可以安全地删除实例
更新2017-12-02 为了回应向github发布此请求的请求,我创建了gist。
更新2018-07-21 我一直在使用声明式样式编程,这种方式现在是我个人的最爱:fiddle,{{3} }
通常情况下,它会处理你想要的情况(ctrl,alt,shift),但是如果你需要同时点击ev_kup
,它就不会“#{1}}。太难了'#34;结合&#34;进入多键查找的方法。
我希望这个彻底解释回答迷你博客很有用:)
答案 1 :(得分:28)
您应该使用 keydown 事件来跟踪按下的键,和您应该使用 keyup 事件来跟踪何时钥匙被释放。
请参阅此示例:http://jsfiddle.net/vor0nwe/mkHsU/
(更新:我在这里复制代码,以防jsfiddle.net保释:) HTML:
<ul id="log">
<li>List of keys:</li>
</ul>
...和Javascript(使用jQuery):
var log = $('#log')[0],
pressedKeys = [];
$(document.body).keydown(function (evt) {
var li = pressedKeys[evt.keyCode];
if (!li) {
li = log.appendChild(document.createElement('li'));
pressedKeys[evt.keyCode] = li;
}
$(li).text('Down: ' + evt.keyCode);
$(li).removeClass('key-up');
});
$(document.body).keyup(function (evt) {
var li = pressedKeys[evt.keyCode];
if (!li) {
li = log.appendChild(document.createElement('li'));
}
$(li).text('Up: ' + evt.keyCode);
$(li).addClass('key-up');
});
在该示例中,我使用数组来跟踪正在按下哪些键。在实际应用程序中,一旦释放了相关密钥,您可能希望delete
每个元素。
请注意,虽然我在这个例子中使用了jQuery让自己变得简单,但在使用“原始”Javascript时,这个概念也同样适用。
答案 2 :(得分:11)
document.onkeydown = keydown;
function keydown (evt) {
if (!evt) evt = event;
if (evt.ctrlKey && evt.altKey && evt.keyCode === 115) {
alert("CTRL+ALT+F4");
} else if (evt.shiftKey && evt.keyCode === 9) {
alert("Shift+TAB");
}
}
答案 3 :(得分:6)
我用这种方式(必须检查Shift + Ctrl按下的位置):
// create some object to save all pressed keys
var keys = {
shift: false,
ctrl: false
};
$(document.body).keydown(function(event) {
// save status of the button 'pressed' == 'true'
if (event.keyCode == 16) {
keys["shift"] = true;
} else if (event.keyCode == 17) {
keys["ctrl"] = true;
}
if (keys["shift"] && keys["ctrl"]) {
$("#convert").trigger("click"); // or do anything else
}
});
$(document.body).keyup(function(event) {
// reset status of the button 'released' == 'false'
if (event.keyCode == 16) {
keys["shift"] = false;
} else if (event.keyCode == 17) {
keys["ctrl"] = false;
}
});
答案 4 :(得分:2)
让keydown甚至调用多个函数,每个函数检查一个特定的键并做出适当的响应。
document.keydown = function (key) {
checkKey("x");
checkKey("y");
};
答案 5 :(得分:2)
$(document).ready(function () {
// using ascii 17 for ctrl, 18 for alt and 83 for "S"
// ctr+alt+S
var map = { 17: false, 18: false, 83: false };
$(document).keyup(function (e) {
if (e.keyCode in map) {
map[e.keyCode] = true;
if (map[17] && map[18] && map[83]) {
// Write your own code here, what you want to do
map[17] = false;
map[18] = false;
map[83] = false;
}
}
else {
// if u press any other key apart from that "map" will reset.
map[17] = false;
map[18] = false;
map[83] = false;
}
});
});
答案 6 :(得分:1)
谁需要完整的示例代码。右+左添加
var keyPressed = {};
document.addEventListener('keydown', function(e) {
keyPressed[e.key + e.location] = true;
if(keyPressed.Shift1 == true && keyPressed.Control1 == true){
// Left shift+CONTROL pressed!
keyPressed = {}; // reset key map
}
if(keyPressed.Shift2 == true && keyPressed.Control2 == true){
// Right shift+CONTROL pressed!
keyPressed = {};
}
}, false);
document.addEventListener('keyup', function(e) {
keyPressed[e.key + e.location] = false;
keyPressed = {};
}, false);
答案 7 :(得分:1)
如果按下其中一个键是Alt / Crtl / Shift,您可以使用此方法:
document.body.addEventListener('keydown', keysDown(actions) );
function actions() {
// do stuff here
}
// simultaneous pressing Alt + R
function keysDown (cb) {
return function (zEvent) {
if (zEvent.altKey && zEvent.code === "KeyR" ) {
return cb()
}
}
}
答案 8 :(得分:1)
我喜欢使用此代码段,它对于编写游戏输入脚本非常有用
var keyMap = [];
window.addEventListener('keydown', (e)=>{
if(!keyMap.includes(e.keyCode)){
keyMap.push(e.keyCode);
}
})
window.addEventListener('keyup', (e)=>{
if(keyMap.includes(e.keyCode)){
keyMap.splice(keyMap.indexOf(e.keyCode), 1);
}
})
function key(x){
return (keyMap.includes(x));
}
function checkGameKeys(){
if(key(32)){
// Space Key
}
if(key(37)){
// Left Arrow Key
}
if(key(39)){
// Right Arrow Key
}
if(key(38)){
// Up Arrow Key
}
if(key(40)){
// Down Arrow Key
}
if(key(65)){
// A Key
}
if(key(68)){
// D Key
}
if(key(87)){
// W Key
}
if(key(83)){
// S Key
}
}
答案 9 :(得分:1)
这是 Bradens 答案的实现。
var keys = {}
function handleKeyPress(evt) {
let { keyCode, type } = evt || Event; // to deal with IE
let isKeyDown = (type == 'keydown');
keys[keyCode] = isKeyDown;
// test: enter key is pressed down & shift isn't currently being pressed down
if(isKeyDown && keys[13] && !keys[16]){
console.log('user pressed enter without shift')
}
};
window.addEventListener("keyup", handleKeyPress);
window.addEventListener("keydown", handleKeyPress);
答案 10 :(得分:0)
我尝试在keypress
上添加Event
keydown
处理程序。 E.g:
window.onkeydown = function() {
// evaluate key and call respective handler
window.onkeypress = function() {
// evaluate key and call respective handler
}
}
window.onkeyup = function() {
window.onkeypress = void(0) ;
}
这只是为了说明一种模式;我不会在这里详细介绍(特别是没有进入浏览器特定的level2 + Event
注册)。
请回复一下这是否有帮助。
答案 11 :(得分:0)
case 65: //A
jp = 1;
setTimeout("jp = 0;", 100);
if(pj > 0) {
ABFunction();
pj = 0;
}
break;
case 66: //B
pj = 1;
setTimeout("pj = 0;", 100);
if(jp > 0) {
ABFunction();
jp = 0;
}
break;
不是最好的方式,我知道。
答案 12 :(得分:0)
这不是通用方法,但在某些情况下很有用。对于 CTRL + something 或 Shift + something 或 CTRL +的组合很有用 Shift + 某物,等等。
示例:当您要使用 CTRL + P 打印页面时,始终按下的第一个键始终是 CTRL ,然后是 P 。与 CTRL + S , CTRL + U 和其他组合相同。
document.addEventListener('keydown',function(e){
//SHIFT + something
if(e.shiftKey){
switch(e.code){
case 'KeyS':
alert('Shift + S');
break;
}
}
//CTRL + SHIFT + something
if(e.ctrlKey && e.shiftKey){
switch(e.code){
case 'KeyS':
alert('CTRL + Shift + S');
break;
}
}
});
答案 13 :(得分:0)
只需使事情更稳定:
var keys = [];
$(document).keydown(function (e) {
if(e.which == 32 || e.which == 70){
keys.push(e.which);
if(keys.length == 2 && keys.indexOf(32) != -1 && keys.indexOf(70) != -1){
alert("it WORKS !!"); //MAKE SOMETHING HERE---------------->
keys.length = 0;
}else if((keys.indexOf(32) == -1 && keys.indexOf(70) != -1) || (keys.indexOf(32) != -1 && keys.indexOf(70) == -1) && (keys.indexOf(32) > 1 || keys.indexOf(70) > 1)){
}else{
keys.length = 0;
}
}else{
keys.length = 0;
}
});
答案 14 :(得分:0)
如果你想用控制键找到任何按键事件,你可以这样做
onkeypress = (e) =>{
console.log(e);
if(e.ctrlKey && e.code == "KeyZ"){
document.write("do somthing")
} }
答案 15 :(得分:-1)
Easiest, and most Effective Method
//check key press
function loop(){
//>>key<< can be any string representing a letter eg: "a", "b", "ctrl",
if(map[*key*]==true){
//do something
}
//multiple keys
if(map["x"]==true&&map["ctrl"]==true){
console.log("x, and ctrl are being held down together")
}
}
//>>>variable which will hold all key information<<
var map={}
//Key Event Listeners
window.addEventListener("keydown", btnd, true);
window.addEventListener("keyup", btnu, true);
//Handle button down
function btnd(e) {
map[e.key] = true;
}
//Handle Button up
function btnu(e) {
map[e.key] = false;
}
//>>>If you want to see the state of every Key on the Keybaord<<<
setInterval(() => {
for (var x in map) {
log += "|" + x + "=" + map[x];
}
console.log(log);
log = "";
}, 300);