我一直在制作一个使用键盘作为输入的在线钢琴。有时,当您按下某个键时,它会注册两个事件而不是一个。
问题是,我尝试使用 e.repeat
(其中 e
是键盘事件)来仅注册第一个 keydown 事件。
您可以尝试通过此临时链接使用它:https://virtual-piano-demo.netlify.app/
总体而言,我对 JavaScript 和 Web 开发还比较陌生,因此我们非常感谢您对我的代码提出任何建议。
if (!e.repeat && isValidInput(e.key)) {
let pitch = keysMap.get(e.key);
playNote(pitch);
document.getElementById(pitch).classList.add("key-bkg-color"); // change color of key
}
这是相关代码。
let notes = document.getElementsByClassName("note");
let soften = document.getElementById("soften");
let sustain = document.getElementById("sustain");
let piano = document.getElementById("piano");
let context = new (window.AudioContext || window.webkitAudioContext)();
let keysMap = new Map([
['q', "C3"],
['2', "Db3"],
['w', "D3"],
['3', "Eb3"],
['e', "E3"],
['r', "F3"],
['5', "Gb3"],
['t', "G3"],
['6', "Ab3"],
['y', "A3"],
['7', "Bb3"],
['u', "B3"],
['i', "C4"],
['9', "Db4"],
['o', "D4"],
['0', "Eb4"],
['p', "E4"],
['[', "F4"],
['=', "Gb4"],
[']', "G4"],
['a', "Ab4"],
['z', "A4"],
['s', "Bb4"],
['x', "B4"],
['c', "C5"],
['f', "Db5"],
['v', "D5"],
['g', "Eb5"],
['b', "E5"],
['n', "F5"],
['j', "Gb5"],
['m', "G5"],
['k', "Ab5"],
[',', "A5"],
['l', "Bb5"],
['.', "B5"],
['/', "C6"],
]);
// load all audio files when page loads so that when the user presses keys the sounds start on time
// store buffers in array to access piano sounds when user presses a key
let buffers = [];
window.addEventListener("load", function() {
for (let i = 0; i < notes.length; ++i) {
let request = new XMLHttpRequest();
request.open("GET", "./audio/" + notes[i].id + ".mp3");
request.responseType = "arraybuffer";
request.onload = function() {
let undecodedAudio = request.response;
context.decodeAudioData(undecodedAudio, (data) => buffers[notes[i].id] = data)
}
request.send();
}
})
function isValidInput(input) {
return keysMap.has(input) === true;
}
function playNote(pitch) {
let playSound = context.createBufferSource();
let gainNode = context.createGain();
playSound.buffer = buffers[pitch];
if (soften.checked === true) {
gainNode.gain.setValueAtTime(0.6, context.currentTime);
gainNode.gain.exponentialRampToValueAtTime(1.05, context.currentTime + 0.35);
}
else {
gainNode.gain.setValueAtTime(1.5, context.currentTime);
}
playSound.connect(gainNode);
gainNode.connect(context.destination);
playSound.start(context.currentTime);
if (sustain.checked === false) {
gainNode.gain.exponentialRampToValueAtTime(0.001, context.currentTime + 3);
}
}
// Keyboard Input
document.addEventListener("keydown", function(e) {
// the pressed key is used for input in the piano -> play note
// !e.repeat makes sure that only the first keydown event when holding down on a certain key is taken into account
if (!e.repeat && isValidInput(e.key)) {
let pitch = keysMap.get(e.key);
playNote(pitch);
document.getElementById(pitch).classList.add("key-bkg-color"); // change color of key
}
})
document.addEventListener("keyup", function(e) {
let pitch = keysMap.get(e.key);
document.getElementById(pitch).classList.remove("key-bkg-color"); // change color of key
})
最佳答案
我很确定这是一个严重的JavaScript错误,我能够重现这个问题,例如你按住Q,然后按住W,然后你在按住W的同时只释放Q,它将触发 W 事件。
这就是我所做的,我创建了一个按下的按键数组,并且只有在松开按键时才能再次播放相应的音高,并且在按下按键时我从按下的按键数组中删除该按键:
我还添加了小写逻辑,因为如果激活大写锁定,钢琴将无法工作
let notes = document.getElementsByClassName("note");
let soften = document.getElementById("soften");
let sustain = document.getElementById("sustain");
let keyAssist = document.getElementById("key-assist");
let piano = document.getElementById("piano");
let context = new (window.AudioContext || window.webkitAudioContext)();
let keysMap = new Map([
["q", "C3"],
["2", "Db3"],
["w", "D3"],
["3", "Eb3"],
["e", "E3"],
["r", "F3"],
["5", "Gb3"],
["t", "G3"],
["6", "Ab3"],
["y", "A3"],
["7", "Bb3"],
["u", "B3"],
["i", "C4"],
["9", "Db4"],
["o", "D4"],
["0", "Eb4"],
["p", "E4"],
["[", "F4"],
["=", "Gb4"],
["]", "G4"],
["a", "Ab4"],
["z", "A4"],
["s", "Bb4"],
["x", "B4"],
["c", "C5"],
["f", "Db5"],
["v", "D5"],
["g", "Eb5"],
["b", "E5"],
["n", "F5"],
["j", "Gb5"],
["m", "G5"],
["k", "Ab5"],
[",", "A5"],
["l", "Bb5"],
[".", "B5"],
["/", "C6"],
]);
// load all audio files when page loads so that when the user presses keys the sounds start on time
// store buffers in array to access piano sounds when user presses a key
let buffers = [];
window.addEventListener("load", function () {
for (let i = 0; i < notes.length; ++i) {
let request = new XMLHttpRequest();
request.open("GET", "./audio/" + notes[i].id + ".mp3");
request.responseType = "arraybuffer";
request.onload = function () {
let undecodedAudio = request.response;
context.decodeAudioData(undecodedAudio, (data) => (buffers[notes[i].id] = data));
};
request.send();
}
});
function isValidInput(input) {
return keysMap.has(input) === true;
}
function playNote(pitch) {
let playSound = context.createBufferSource();
let gainNode = context.createGain();
playSound.buffer = buffers[pitch];
if (soften.checked === true) {
gainNode.gain.setValueAtTime(0.4, context.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.85, context.currentTime + 0.35);
} else {
gainNode.gain.setValueAtTime(1.5, context.currentTime);
}
playSound.connect(gainNode);
gainNode.connect(context.destination);
playSound.start(context.currentTime);
if (sustain.checked === false) {
gainNode.gain.exponentialRampToValueAtTime(0.001, context.currentTime + 3);
}
}
var currentlyPressedKeys = []; //<========== ADDED HERE ###########
// Keyboard Input
document.addEventListener("keydown", function (e) {
let key = e.key.toLowerCase();
if (!e.repeat && isValidInput(key) && !currentlyPressedKeys.includes(key)) { //<========== ADDED HERE ###########
let pitch = keysMap.get(key);
playNote(pitch);
document.getElementById(pitch).classList.add("key-bkg-color");
currentlyPressedKeys.push(key); //<========== ADDED HERE ###########
}
});
document.addEventListener("keyup", function (e) {
let key = e.key.toLowerCase();
let pitch = keysMap.get(key);
if (document.getElementById(pitch)) {
document.getElementById(pitch).classList.remove("key-bkg-color");
}
currentlyPressedKeys = currentlyPressedKeys.filter((pressedKey) => pressedKey != key); //<========== ADDED HERE ###########
});
// Mouse Input
let isDown;
document.activeElement.addEventListener("mousedown", function (e) {
if (e.target.tagName === "BUTTON") {
playNote(e.target.id);
e.target.classList.add("key-bkg-color");
isDown = true;
}
});
document.activeElement.addEventListener("mouseup", function (e) {
e.target.classList.remove("key-bkg-color");
isDown = false;
});
piano.addEventListener("mouseover", function (e) {
if (e.target.tagName == "BUTTON" && isDown === true) {
playNote(e.target.id);
e.target.classList.add("key-bkg-color");
}
});
piano.addEventListener("mouseout", function (e) {
e.target.classList.remove("key-bkg-color");
});
// Label Notes
keyAssist.addEventListener("click", function () {
if (this.checked) {
for (let i = 0; i < notes.length; ++i) {
notes[i].style.fontSize = "1.8rem";
}
} else {
for (let i = 0; i < notes.length; ++i) {
notes[i].style.fontSize = "0";
}
}
});
// Menu Open Animation
let body = document.querySelector("body");
let menuBtn = document.getElementById("menu");
let isOpen = false;
menuBtn.addEventListener("click", function () {
if (!isOpen) {
body.style.left = "-30rem";
isOpen = true;
} else {
body.style.left = "0";
isOpen = false;
}
});
关于javascript - 即使使用 !event.repeat,键盘事件也会触发两次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72960064/