Skip to content

谈谈H5中的audio的播放问题 #4

@impeiran

Description

@impeiran

前言

本文主要针对H5中,使用自定义UI套audio元素,用js控制audio的场景(即不使用自带的control面板)。

播放

<template>
  <div>
    <button @click="playAudio">播放</button>
    <audio src="//xxx.mp3" ref="audio"></audio>
  </div>
</template>

<script>
export default {
  methods: {
    playAudio () {
      this.$refs.audio.play()
    }
  }
}
</script>

上述代码无论在h5还是pc中,都是可行的,因为h5中默认规定在用户交互操作里播放audio。我们在点击事件的回调中进行audioplay,然后请求资源进行播放,符合常理。

但是大多数场景下,audiosrc是动态获取确定的,而播放也不一定是在点击的回调里触发,可能当中经过很多js逻辑,如下面用搭配vuex的例子。

<template>
  <div class="ui-player-audio">
    <audio ref="audio" :src="currentAudio.src"></audio>
  </div>
</template>

<script>
import { mapGetters } from 'vuex'
export default {
  computed: {
    ...mapGetters(['playing', 'currentAudio'])
  },
  
  watch: {
    playing (status) {
      if (status) {
        this.$refs.audio.play()
      } else {
        this.$refs.audio.pause()
      }
    }
  }
}
</script>

这是一个很典型的播放器业务组件,这时候当我们在别处点了某个音频组件,触发vuexdispatch('PLAY_AUDIO', audioInfo)后,组件监听到状态即会播放。但此处会变成h5浏览器认为是不在用户交互下进行播放,而不会按预期进行。

ps: 这样的设计确实有一定的道理,不然进入某些恶意h5,随便就播放音频,会很讨用户厌恶。

Hack方法:

我们在用户第一次触碰到页面的时候,回调执行加载audioload方法,让浏览器认为是在用户交互操作下触发。

/**
 * 模拟交互操作audio元素
 * @param {Element} audioElement
 * @param {Function} callback?
 */
const interactiveAudio = (audioElment, callback) => {
  const _body = document.documentElement
  const handler = e => {
    e.stopPropagation()
    audioElment && audioElment.load()
    callback && callback()
    _body.removeEventListener('touchstart', handler)
  }
  _body.addEventListener('touchstart', handler)
}

Demo代码可更改为:

<script>
import interactiveAudio from '@/utils/interactiveAudio'
import { mapGetters } from 'vuex'
export default {
  computed: {
    ...mapGetters(['playing', 'currentAudio'])
  },
  
  watch: {
    playing (status) {
      if (status) {
        this.$refs.audio.play()
      } else {
        this.$refs.audio.pause()
      }
    }
  },
  
  mounted () {
    interactiveAudio(this.$refs.audio)
  }
}
</script>

自动播放

设置audioautoplay属性,在h5中是行不通的,理由跟上面一样。在目前为止只看到了微信的Webview浏览器中可以做到,因为其注入了一个钩子事件WeixinJSBridgeReady

 document.addEventListener("WeixinJSBridgeReady", function () {
   const audio = document.getElementById('audio')
   audio.play()
 });

而非微信浏览器的环境,只能通过第一次交互触摸回调,进行播放,这里我们复用上述的interactiveAudio函数

const audio = document.getElmentById('audio')
interactiveAudio(audio, () => audio.play())

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions