监听键盘事件(keyup/keydown):Mac OS浏览器中条件语句为何失效?
问题描述
开发了一款开源音乐应用,核心功能是按下键盘按键播放对应预定义音符。通过监听keydown事件,在处理函数中用if (freq && !runningOscs[freq])判断,借助存储运行中振荡器的runningOscs对象(格式{440: osc})避免按住按键时重复播放音符。
但在Mac OS全系列浏览器中,该条件判断失效,按住按键会连续重复播放音符。已精简最小复现示例并通过BrowserStack测试,所有Mac OS+浏览器组合均存在此问题。
尝试添加e.repeat判断拦截重复事件,但在Mac OS Ventura的Chrome v110中,按住按键时e.repeat始终返回false,无法解决问题。
解决方案
放弃依赖e.repeat,改为手动维护按键状态追踪对象,直接记录哪些按键处于按下状态,从根源避免重复触发播放逻辑。
修改后的完整代码如下:
console.log("Yo! There! Mac O'"); // 实例化音频上下文 const audioCTX = new AudioContext(); const freq = 440; // 振荡器频率 let runningOscs = {}; // 存储运行中的振荡器 let pressedKeys = {}; // 新增:追踪已按下的按键 function playNote(freq) { const osc = audioCTX.createOscillator(); osc.frequency.value = freq; // 修正原代码中频率不匹配的问题 osc.type = "sine"; const vol = audioCTX.createGain(); vol.gain.value = 1; runningOscs[freq] = osc; console.log(runningOscs); runningOscs[freq].connect(vol).connect(audioCTX.destination); runningOscs[freq].start(); } // 事件监听 window.addEventListener("keydown", keyDownHandler, false); window.addEventListener("keyup", keyupHandler, false); function keyDownHandler(e) { // 检查当前按键是否已被记录为按下状态 if (pressedKeys[e.key]) return; // 标记按键为已按下 pressedKeys[e.key] = true; if (freq && !runningOscs[freq]) { playNote(freq); console.log(runningOscs); } } function keyupHandler(e) { console.log("key up!"); // 清除按键的按下状态 delete pressedKeys[e.key]; if (freq && runningOscs[freq]) { runningOscs[freq].stop(); delete runningOscs[freq]; } }
<!DOCTYPE html> <p>press a key to play a note</p> <p>holding the key down</p> <p>should play only one instance</p>
关键改动说明
- 新增
pressedKeys对象:专门记录当前处于按下状态的按键,键名为键盘事件的e.key值(也可根据需求改用e.code实现更精准的按键匹配)。 - 修改
keydown逻辑:触发时先检查pressedKeys中是否已存在当前按键,若存在则直接返回,避免重复执行播放;若不存在则标记为已按下,再执行原有的音符播放判断。 - 修正频率不匹配问题:原代码中
playNote函数硬写了osc.frequency.value = 220,和传入的freq=440不一致,现在改为使用传入的freq参数。
这种方式不依赖浏览器的e.repeat属性,完全通过本地状态控制,能在所有平台的浏览器中稳定生效,解决Mac OS上的重复播放问题。
内容的提问来源于stack exchange,提问作者Fishbite
相关产品推荐
相关产品推荐

