尝试使用 Howler 在 keydown 上播放声音 - AudioContext is not allowed to start 错误

问题描述

我正在尝试构建一个鼓垫应用程序,并且大部分已经完成,除了我在按键时遇到的问题。我已经设置为当您按下某些键时,会显示特定文本并播放特定声音。这与鼠标点击完美配合,但是当我尝试使用键盘按钮时,它只显示文本,并给我这个错误

不允许启动 AudioContext。它必须在页面上的用户手势之后恢复(或创建)。 @howler.js:2500

我正在使用以 Howler 作为依赖项的 useSound 钩子,这可能存在问题,但我不确定。这是文档的链接https://github.com/joshwcomeau/use-sound

这是一个代码沙盒构建:https://codesandbox.io/s/drum-pad-jc9b5?file=/src/drumPad.css

完整代码如下:

      useEffect(() =>{document.addEventListener("keydown",key)},[]
  )
  function key(e) {
    // This runs the playThis() function when the desired keys are pressed,but the sound doesn't play. 
    let i = 0  
    let keycodes = drumBank.map(function(y,x){
        if (y.keyTrigger === e.key) {
          i = x
          playThis(i)
          
        }})
  } 

  const [powerOn,setPowerOn] = useState(true)
  const [text,setText] = useState("");
  const [Sound0] = useSound(drumBank[0].url) 
  const [Sound1] = useSound(drumBank[1].url) 
  const [Sound2] = useSound(drumBank[2].url) 
  const [Sound3] = useSound(drumBank[3].url) 
  const [Sound4] = useSound(drumBank[4].url) 
  const [Sound5] = useSound(drumBank[5].url) 
  const [Sound6] = useSound(drumBank[6].url) 
  const [Sound7] = useSound(drumBank[7].url) 
  const [Sound8] = useSound(drumBank[8].url)
  

  


 function playThis(num) {
  if (powerOn === true) 
         { setText(drumBank[num].id)
          switch(num) {
          case 0: Sound0()
          break;
          case 1: Sound1()
          break;
          case 2: Sound2()
          break;
          case 3: Sound3()
          break;
          case 4: Sound4()
          break;
          case 5: Sound5()
          break;
          case 6: Sound6()
          break;
          case 7: Sound7()
          break;
          case 8: Sound8()
          break;
        }
      }
        else {setText("Power is off")}
      } 


 
return (
    <div id="drum-machine" className="drumpad-container" >
        <h3 id={powerOn ? "drum-power-text-on" : "drum-power-text-off"}>Power: </h3>
        <div id="drum-power-div" onClick={() => setPowerOn(!powerOn)}>
          <div id={powerOn ? "drum-power-button-on" : "drum-power-button-off"}>

          </div>
        </div>
        <div id="display" className="drumpad-display">
            <p className="drumpad-text">{text}</p>
        </div>
        <button className="drum-pad" id="drum-pad-1" onClick={() => playThis(0)} >Q</button>
        <button className="drum-pad" id="drum-pad-2" onClick={() => playThis(1)}>W</button>
        <button className="drum-pad" id="drum-pad-3" onClick={() => playThis(2)}>E</button>
        <button className="drum-pad" id="drum-pad-4" onClick={() => playThis(3)}>A</button>
        <button className="drum-pad" id="drum-pad-5" onClick={() => playThis(4)}>S</button>
        <button className="drum-pad" id="drum-pad-6" onClick={() => playThis(5)}>D</button>
        <button className="drum-pad" id="drum-pad-7" onClick={() => playThis(6)}>Z</button>
        <button className="drum-pad" id="drum-pad-8" onClick={() => playThis(7)}>X</button>
        <button className="drum-pad" id="drum-pad-9" onClick={() => playThis(8)}>C</button>
        </div>
)

}

key 函数也以某种方式绕过了我的电源功能,即使在电源关闭时应该停用 playThis() 也会显示文本。

如果有人能帮我弄清楚如何在按键上播放声音,我将不胜感激。

解决方法

我想通了。如果将 useSound 钩子放在函数内部,则它会在按键时正常播放声音,然后调用该函数而不是钩子本身。所以,不要把这些放在 playThis() 中:

  const [Sound0] = useSound(drumBank[0].url) 
  const [Sound1] = useSound(drumBank[1].url) 
  const [Sound2] = useSound(drumBank[2].url) 
  const [Sound3] = useSound(drumBank[3].url) 
  const [Sound4] = useSound(drumBank[4].url) 
  const [Sound5] = useSound(drumBank[5].url) 
  const [Sound6] = useSound(drumBank[6].url) 
  const [Sound7] = useSound(drumBank[7].url) 
  const [Sound8] = useSound(drumBank[8].url)

我将这些常量放入它们各自的函数中,如下所示:

  const [Sound0] = useSound(drumBank[0].url) 


  const [Sound1] = useSound(drumBank[1].url) 
  const [Sound2] = useSound(drumBank[2].url) 
  const [Sound3] = useSound(drumBank[3].url) 
  const [Sound4] = useSound(drumBank[4].url) 
  const [Sound5] = useSound(drumBank[5].url) 
  const [Sound6] = useSound(drumBank[6].url) 
  const [Sound7] = useSound(drumBank[7].url) 
  const [Sound8] = useSound(drumBank[8].url)
  let audio0 = () => {sound0()}
  let audio1 = () => {sound1()}
  let audio2 = () => {sound2()}
  let audio3 = () => {sound3()}
  let audio4 = () => {sound4()}
  let audio5 = () => {sound5()}
  let audio6 = () => {sound6()}
  let audio7 = () => {sound7()}
  let audio8 = () => {sound8()}

然后在我的 playThis() 函数中,我将 audio0() 而不是 sound0(),然后它就可以工作了。像这样:

 function playThis(num) {
  if (powerOn === true) 
         { setText(drumBank[num].id)
          switch(num) {
          case 0: audio0()
          break;
          case 1: audio1()
          break;
          case 2: audio2()
          break;
          case 3: audio3()
          break;
          case 4: audio4()
          break;
          case 5: audio5()
          break;
          case 6: audio6()
          break;
          case 7: audio7()
          break;
          case 8: audio8()
          break;
        }
      }
        else {setText("Power is off")}
      } 

至于绕过电源按钮的按键,我只使用了一个 useKeyPress 插件,而且效果很好。