尝试在 iOS 浏览器上通过 JS 播放 .mp3?

Trying to play an .mp3 via JS on iOS browsers?

我尝试使用 HTML 和 JS 制作节拍器。我知道由于限制,音频无法自动播放(无论如何我都不希望它自动播放),所以我放置了一个 <audio ...> 元素,里面有一个 <source ...> ,所有(我想?)适当的属性;我使用通过单击按钮触发的 JS 控制播放。这适用于我的笔记本电脑,甚至适用于 XBox Edge 浏览器,但在 iOS 浏览器(Safari 和 Firefox)上无法播放声音。 HTML 看起来像这样:

<button id="metronome-button">Start</button>
<audio id="metronome-audio" autoplay="0" autostart="0" loop="0">
  <source src="tick.mp3" type="audio/mpeg">
</audio>

JS 看起来像这样:

const metronomeAudio = document.getElementById('metronome-audio');
const metronomeButton = document.getElementById('metronome-button');
let metronomeInterval = null;

metronomeButton.addEventListener('click', () => {    
    metronomeInterval = setInterval(() => {
      metronomeAudio.loop = false;
      metronomeAudio.pause();
      metronomeAudio.play();
    }, 500);
});

因为这不起作用,我做了更多的查找并在另一个 Whosebug 线程中找到了这个解决方案,它使用 JS 而根本没有 HTML(除了被按钮的 click 触发)事件):

function startMetronome () {
  setInterval(() => {
    let audio = new Audio('tick.mp3');
    audio.loop = false;
    audio.play();
    audio.onended = () => {
      audio.remove();
    }
  }, 500);  
}  

同样,这在 PC 上适用于各种浏览器,为什么 特别是 在 iOS 上失败? (没有在 Android 上测试过,没有设备。)

我不确定这是否在任何地方都有记录(我没有偶然发现),但经过一些额外的测试后我发现 iOS 禁止在 .play() 上使用函数=28=]现有HTML<audio>元素或JSAudio()元素,除非直接和同步调用作为a的结果用户 UI 事件。换句话说,这将起作用:

const button = document.getElementById('my-button')
const audio = document.getElementById('my-audio')
button.addEventListener('click', () => {
    audio.play()
})

但这行不通:

const button = document.getElementById('my-button')
const audio = document.getElementById('my-audio')
button.addEventListener('click', () => {
    setTimeout(() => { audio.play() }, 1000)
})

因此,在异步回调中调用 .play() 会中断 iOS 上的播放。

但是,如此处对此答案的评论中所述:,如果您在同步用户交互事件处理程序中实例化音频元素,则可以重复使用和重新播放(例如 .play()) Audio() 元素随你喜欢。例如:

const button = document.getElementById('my-button')
let audio = null
button.addEventListener('click', () => {
    audio = new Audio('myAudio.mp3')
    // Works because `audio` itself was created synchronously during user event handler
    setTimeout(() => { audio.play() }, 1000)
})