马黑黑 发表于 2024-2-26 12:19

处理audio控件能否自动播放的另一种机制

本帖最后由 马黑黑 于 2024-2-26 21:28 编辑 <br /><br /><style>
        .mama { font-size: 18px; }
        .mama p { margin: 10px 0; }
        .mama mark { padding: 2px 6px; background: lightblue; }
        .mum { position: relative; margin: 0; padding: 10px; font: normal 16px/20px Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; color: black; background: rgba(240, 240, 240,.95); box-shadow: 2px 2px 4px gray; border: thin solid lightblue; border-radius: 6px; }
        .mum ::selection { background-color: rgba(0,100,100,.35); }
        .mum div { margin: 0; padding: 0; }
        .mum cl-cd { display: block; position: relative; margin: 0 0 0 50px; padding: 0 0 0 10px; white-space: pre-wrap; overflow-wrap: break-word; border-left: 1px solid silver; }
        .mum cl-cd::before { position: absolute; content: attr(data-idx); width: 50px; color: gray; text-align: right; transform: translate(-70px); }
        .tRed { color: red; }
        .tBlue { color: blue; }
        .tGreen { color: green; }
        .tDarkRed { color: darkred; }
        .tMagenta { color: magenta; }
</style>

<div class="mama">

<p>不久前我们在音频播放器系列开发教程里接触过有关audio音频控件的自动播放问题,我们使用<mark>autoplay 属性 + audioElement.paused</mark>来解决问题,事实证明这是可行的:<mark>audioElement.paused</mark>能让我们获得audio控件是否处于暂停状态,再通过对audio控件的其它几个事件进行监听,从而构造出几近完美的音频与音画的动态联动。autoplay的缺陷是,它常会被浏览器的限制策略所阻止,且不会报错,浏览器在控制台一般也不会有提示(个别浏览器可能产生非错误级别的友好提示)。autoplay与限制策略间的此等关系对页面设计者而言多少是一种尴尬,所以,应该设计另外一种监听机制,以帮助设计者了解更多的audio控件在开始运行时的状况。</p>
<p>其实,除了依据paused状态,确实还存在另外的应对限制策略的机制,那就是Promise API,一个现代浏览器普遍支持的基于限制策略的接口。与对待 autoplay 属性不同,现代浏览器在audio控件首次发起播放即<mark>audioElement.play()</mark>动作的时候会触发Promise对此动作做出策略响应,且能返回错误代码,我们还可以从中获取到错误的名称——浏览器禁止自动播放时该错误名称为NotAllowedError。依此Promise机制,我们可以获知浏览器允许或禁止自动播放,进而做相应处理。</p>
<p>以下代码,我们不考虑旧浏览器对Promise是否支持,仅针对现代浏览器:</p>
<div class="mum" id="mum">
<cl-cd data-idx="1">&lt;div <span class="tRed">id</span>=<span class="tMagenta">"mydiv"</span>&gt;</cl-cd>
<cl-cd data-idx="2">&nbsp; &nbsp; <span class="tGreen">&lt;!-- audio不设置autoplay --&gt;</span></cl-cd>
<cl-cd data-idx="3">&nbsp; &nbsp; &lt;audio <span class="tRed">id</span>=<span class="tMagenta">"aud"</span> src=<span class="tMagenta">"https://music.163.com/song/media/outer/url?<span class="tRed">id</span>=408532724"</span> loop&gt;&lt;/audio&gt;</cl-cd>
<cl-cd data-idx="4">&lt;/div&gt;</cl-cd>
<cl-cd data-idx="5"><br></cl-cd>
<cl-cd data-idx="6">&lt;script&gt;</cl-cd>
<cl-cd data-idx="7"><span class="tBlue">let</span> autoplayPromise = aud.play(); <span class="tGreen">// 命令audio播放并获取Promise对象</span></cl-cd>
<cl-cd data-idx="8"><br></cl-cd>
<cl-cd data-idx="9">autoplayPromise.then(() =&gt; {</cl-cd>
<cl-cd data-idx="10">&nbsp; &nbsp; console.log(<span class="tMagenta">'ok'</span>); <span class="tGreen">//正常播放, 打印ok</span></cl-cd>
<cl-cd data-idx="11"><span class="tGreen">//俘获错误</span></cl-cd>
<cl-cd data-idx="12">}).<span class="tBlue">catch</span>(err =&gt; {</cl-cd>
<cl-cd data-idx="13">&nbsp; &nbsp; console.log(err.name); <span class="tGreen">// → NotAllowedError</span></cl-cd>
<cl-cd data-idx="14">&nbsp; &nbsp; aud.controls = true; <span class="tGreen">//令audio界面显示出来让用户手动操作</span></cl-cd>
<cl-cd data-idx="15">});</cl-cd>
<cl-cd data-idx="16"><br></cl-cd>
<cl-cd data-idx="17"><span class="tGreen">//移除audio界面</span></cl-cd>
<cl-cd data-idx="18">aud.onmouseout = () =&gt; aud.controls = false;</cl-cd>
<cl-cd data-idx="19">&lt;/script&gt;</cl-cd>
</div>
<p><button id="btnShow" type="button" value="show">运行代码</button></p>
<div id="showBox"></div>
<p>上述代码,我们使用链式方法处理 autoplayPromise 对象实例(autoplayPromise是我们自定义的变量名),该对象如前已述,它是一个 Promise 实例,因为 audioElement.play 会返回一个 Promise 对象副本。autoplayPromise.then 意为,如若 autoplayPromise 正常,那么(then就是那么的意思),做点什么,示例中是在控制台打印OK,然后 .catch 是俘获错误信息,我们打印出错误的名称,接着呈现audio界面。最后,用户不论手动播放音频与否,鼠标指针从播放器界面上移开后,我们让播放器界面消失。这个示例仅是演示,若使用于生产环境,需要做一些额外工作,以提升页面与用户交互的友好性。</p>
<p>有意思的是,在本帖运行示例代码,播放器都能成功播放,不论浏览器限制策略是否禁止自动播放。这是为什么呢?原因很简单,我们点击了“运行代码”按钮才去运行 aud.play(),浏览器就认为这是用户与页面已经做了交互、是用户许可播放音频的,aud.play 触发的 Promise 限制策略故此放行。若示例代码单独保存为一个HTML文档,情形则不一样:如果不允许自动播放,则 Promise 不会放行,演示的代码就会显示出audio界面;反之则自动播放音乐。</p>
<p>更有意思的是,当audio控件处于静音状态(muted)或音量为0(volume=0)时,限制策略也会放行audio的音频自动播放,一些实现音频自动播放的方法基于此特性,然后会引导用户解除静音状态或调解音量。这方法有点另类,且不是十分牢靠:严格机制下的限制策略,静音与音量为零是音频一样不可以自动播放,一切依赖于当前浏览器的限制级别与用户对浏览器的相关设置。所以还是不要痴心妄想,老老实实采取理性的做法:允许自动播放就自动播放,不允许就将选择权交给用户。</p>

</div>

<script>

btnShow.onclick = function() {
        if(this.value === 'show') {
                runCode(mum.innerText, showBox);
                this.value = 'shut';
                this.textContent = '关闭运行';
        }else{
                showBox.innerHTML = '';
                this.value = 'show';
                this.textContent = '运行代码';
        }
};

function runCode(str,ele) {
        var reg = /(<script(.*?)>)(.|\n)*?(<\/script>)/g;
        var js_str, html_str;
        if(str.match(reg) !== null) {
                js_str = str.match(reg);
                html_str = str.replace(js_str, '').trim();
                js_str = js_str.replace(/<[\/]{0,1}script[^>]*>/g,'').trim();
        } else {
                js_str = '';
                html_str = str.trim();
        }
        ele.innerHTML = html_str;
        var myfunc = new Function(js_str);
        myfunc();
};

</script>

红影 发表于 2024-2-26 14:45

“autoplay的缺陷是,它常会被浏览器的限制策略所阻止,且不会报错,浏览器在控制台一般也不会有提示”

有些电脑会碰到这样的问题,有些不会。已经感受到了{:4_173:}

红影 发表于 2024-2-26 14:46

黑黑的研究真细致,设计方方面面呢,厉害{:4_199:}

樵歌 发表于 2024-2-26 15:45

坚持把瓜吃完,看冬小雨来不{:4_203:}

南无月 发表于 2024-2-26 18:23

这个十分智能
若示例代码单独保存为一个HTML文档,情形则不一样:如果不允许自动播放,则 Promise 不会放行,演示的代码就会显示出audio界面;反之则自动播放音乐。
试了一下。。。好玩得很{:4_173:}

马黑黑 发表于 2024-2-26 19:36

南无月 发表于 2024-2-26 18:23
这个十分智能
若示例代码单独保存为一个HTML文档,情形则不一样:如果不允许自动播放,则 Promise 不会放 ...

谢玩

马黑黑 发表于 2024-2-26 19:36

樵歌 发表于 2024-2-26 15:45
坚持把瓜吃完,看冬小雨来不

瓜皮都不剩了,来有啥用{:4_170:}

马黑黑 发表于 2024-2-26 19:37

红影 发表于 2024-2-26 14:45
“autoplay的缺陷是,它常会被浏览器的限制策略所阻止,且不会报错,浏览器在控制台一般也不会有提示”

...

{:4_181:}

马黑黑 发表于 2024-2-26 19:37

红影 发表于 2024-2-26 14:46
黑黑的研究真细致,设计方方面面呢,厉害

没有没有,还是很粗糙

红影 发表于 2024-2-26 19:41

马黑黑 发表于 2024-2-26 19:37


现在能给出失败原因,真的很棒的{:4_187:}

红影 发表于 2024-2-26 19:41

马黑黑 发表于 2024-2-26 19:37
没有没有,还是很粗糙

黑黑谦虚,对这个之前一点都不知道呢,学习了{:4_187:}

马黑黑 发表于 2024-2-26 19:43

红影 发表于 2024-2-26 19:41
黑黑谦虚,对这个之前一点都不知道呢,学习了

我这个处理确实是很偷懒的。你看网易云音乐播放器,它的代码那才叫多,多到你没有办法阅读,当然,它解决了所有限制实现了自动播放,目前没有几个大厂能做的那么好。

马黑黑 发表于 2024-2-26 19:44

红影 发表于 2024-2-26 19:41
现在能给出失败原因,真的很棒的

这个可以给,不用promise也可以给

南无月 发表于 2024-2-26 19:52

马黑黑 发表于 2024-2-26 19:36
谢玩

{:4_170:}歌是真的好听。。。

红影 发表于 2024-2-26 20:10

马黑黑 发表于 2024-2-26 19:43
我这个处理确实是很偷懒的。你看网易云音乐播放器,它的代码那才叫多,多到你没有办法阅读,当然,它解决 ...

对那个不懂啊,从来没去看过呢{:4_173:}

红影 发表于 2024-2-26 20:11

马黑黑 发表于 2024-2-26 19:44
这个可以给,不用promise也可以给

问题是怎么给一个都不知道呢{:4_173:}

马黑黑 发表于 2024-2-26 21:19

红影 发表于 2024-2-26 20:11
问题是怎么给一个都不知道呢

那个保姆级教程不是说了吗

马黑黑 发表于 2024-2-26 21:19

红影 发表于 2024-2-26 20:10
对那个不懂啊,从来没去看过呢

听过,偷过,就从来没想过人家总是可以自动播放的{:4_170:}

马黑黑 发表于 2024-2-26 21:23

南无月 发表于 2024-2-26 19:52
歌是真的好听。。。

不错的,这是德国一名音乐家的歌曲,1956年出生,从小就对音乐有特殊的爱好和理解,所创造的曲子都是经典的

樵歌 发表于 2024-2-26 21:25

马黑黑 发表于 2024-2-26 19:36
瓜皮都不剩了,来有啥用

下次给他留下一小片{:4_189:}
页: [1] 2 3 4
查看完整版本: 处理audio控件能否自动播放的另一种机制