这个问题困扰我有一段时间了。我正在 Javascript 中使用 WebAudioAPI 来制作合成器。我有一个播放声音的 keydown 事件和一个停止声音的 keyup 事件。但是,如果我按住两个键,首先是 q,然后是 w,如果我松开 q(按住 w),w 的事件会第二次触发,留下一个从未播放过的附加声音。即使在触发 keyup 事件后也会结束。
这是按键事件:
document.body.addEventListener('keydown', (e) => {
if(e.repeat){
return;
}
else if (this.heldKeys.length > 0){
let keyIsHeld;
for (let key of this.heldKeys){
if (key.code == e.code){
this.eventStop(key, true);
keyIsHeld = true;
}
}
if(keyIsHeld){
this.removeElementsFromArrayByProperty(this.heldKeys, e);
}
}
this.eventStart(e, true);
this.heldKeys.push(e);
});
注意
if (e.repeat)
子句。这非常适合按住该键并且不会重复,但是一旦按下另一个键并且释放第一个键,天哪,我们就会触发另一个事件。
我尝试了几种方法来捕获这些重复的声音(就像上面看到的 else if 语句一样),但即使它有效(这里没有),事件仍然会触发,并且会出现中断声音。
我已经做了相当多的谷歌搜索,但我回来的只是添加
e.repeat
语句(或者与按下的键的 bool 非常相似的东西),但是这些都没有能力阻止晚宴上看不见的(但声音很大)的人,每次弹奏两个键并且放开第一个键时,他都会闯入。
我希望对此有一个非常明显的答案,比如也许使用 html 元素,但我无法理解是什么让两个事件以按下一个键的价格触发(在其前身的帮助下)。
顺便说一句,我已经用多个键盘尝试过这一点。结果相同。
这是 keydown 事件(如果有帮助的话):
document.body.addEventListener('keyup', (e) => {
this.eventStop(e, true);
this.removeElementsFromArrayByProperty(this.heldKeys, e);
});
这是一个堆栈片段,您可以使用它来复制我收到的事件:
document.body.addEventListener('keydown', (e) => {
if (!e.repeat) {
console.log(e.type, e.key);
}
});
document.body.addEventListener('keyup', (e) => {
console.log(e.type, e.key);
});
To replicate:
<ol>
<li>Click here to focus the document</li>
<li>Press and hold <kbd>q</kbd></li>
<li>Press and hold <kbd>w</kbd></li>
<li>Let go of <kbd>q</kbd></li>
</ol>
After the <code>keyup</code> for <kbd>q</kbd>, there's a spurious non-repeat <code>keydown</code> for <kbd>w</kbd>.
记住 @Kaddath 向我们展示的内容,当触发事件的键被按下时,事件的
repeat
属性被设置为 false
,同时另一个键也被按住并抬起以触发其 keyup
事件;我编辑了 keyup
和 keydown
事件处理程序,以便在触发此附加事件时返回 keydown
。
document.body.addEventListener('keydown', (e) => {
let isKeyAlreadyPressed;
for (let key of this.heldKeys){
if (key.code == e.code){
isKeyAlreadyPressed = true;
}
}
if(e.repeat || isKeyAlreadyPressed){
return;
}
else{
this.eventStart(e, true);
this.heldKeys.push(e);
}
});
document.body.addEventListener('keyup', (e) => {
this.eventStop(e, true);
this.heldKeys = this.removeElementsFromArrayByProperty(this.heldKeys, "code", e.code);
});
在
keydown
事件结束时,我们将该事件添加到名为 heldKeys
的数组中。
在
keyUp
中,heldKeys
数组中与 code
具有相同 e
属性的每个元素都将从数组中删除,因此我们知道它不再被保留。
当下一次触发
keyDown
时,我们检查 heldKeys
中是否存在与 code
的 e
匹配的 code
事件。如果这是真的,那么已经有一个相同类型的键被按下,因此当这个附加事件触发时,可以防止它永无休止的多余声音破坏我们的耳朵。
这不是一个正确的解决方案,但我实际上还没有看到任何人发布解决方案,所以我想在这里发布此内容,以防有人想要快速解决类似问题。
这是项目顺便说一句(大部分已完成,目前正在针对 React 和 TypeScript 进行重组)。