HTML5中Audio使用踩坑汇总

栏目: Html5 · 发布时间: 7年前

阅读 18

HTML5中Audio使用踩坑汇总

  • Cannot read property 'catch' of undefined

    原因:在调用play()时,现代浏览器返回的是一个promise,对于执行失败的,会触发一个Unhandled Promise Rejection,但是对于低版本的浏览器,调用play()并不会返回一个promise。

    解决:应该在调用play()时做如下处理,增加对playPromise的判断

    var playPromise = document.querySelector('video').play();
    
    // In browsers that don’t yet support this functionality,
    // playPromise won’t be defined.
    if (playPromise !== undefined) {
      playPromise.then(function() {
        // Automatic playback started!
      }).catch(function(error) {
        // Automatic playback failed.
        // Show a UI element to let the user manually start playback.
      });
    }
    复制代码

    参考资料: HTMLMediaElement.play() Returns a Promise

  • InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable

    原因:对于还没有设置src的audio,就直接设置currentTime是会触发一个INVALID_STATE_ERR异常的。即使是设置currentTime = 0也会触发这个异常

    解决:在设置currentTime之前,必须先设置audio的src

    参考资料: Offsets into the media resource

    media . currentTime [ = value ]

    Returns the current playback position , in seconds.

    Can be set, to seek to the given time.

    Will throw an INVALID_STATE_ERR exception if there is no selectedmedia resource. Will throw an INDEX_SIZE_ERR exception if the given time is not within the ranges to which the user agent can seek.

  • NotAllowedError

    原因:在调用play()时可能会触发一个NotAllowedError的reject,原因是因为浏览器在某些情况下播放失败,常见场景是,未通过点击的情况下调用play() ,或者点击事件的回调中是在下一个tick里调用的play,例如在setTimeout里调用的play,再或者新创建了很多个audio元素,但是并不是每个audio都是通过用户点击来调用的play()等等。

    • 场景一:未通过点击等事件绑定,直接调用play(),触发NotAllowedError。解决方法,把调用play()的部分放在事件回调里,如下代码:

      playButton.addEventListener("click", () => {
          audioElem.play()
      }, false);
      复制代码
    • 场景二:在点击事件回调中的下一个tick里调用play(),这种情况的示例代码如下,

      // 错误代码示例
      playButton.addEventListener("click", () => {
          setTimeout(() => {
              audioElem.play()
          }, 100)
      }, false);
      复制代码

      这种情况,某些版本【在iOS12.0.1亲测有坑】也会触发NotAllowedError异常,应该避免这种情况,可以考虑如下hack手段解决

      // hack 
      playButton.addEventListener("click", () => {
          audioElem.muted = true
          let p = audioElem.play()
          if (p !== undefined) {
              p.then(() => {
                  audioElem.muted = false
                  audioElem.pause()
                  setTimeout(() => {
                      audioElem.play()
                  }, 100)
              }).catch((e) => {
                  console.log(e)
              })
          }
      }, false);
      复制代码
    • 场景三:创建了多个audio元素,但是并不是每个audio都是通过用户点击来调用的play()的,这时候某些版本【在iOS12.0.1亲测有坑】也会触发NotAllowedError异常。对于这种情况,最好的办法就是只创建一个audio元素,后面通过改变src来播放不同的音乐资源。只要audio通过了事件回调里调用过play,后续都可以直接调用play了,而无需再次绑定事件回调里去执行,并且这样也可以避免创建多个audio来减少内存使用。

      playButton.addEventListener("click", () => {
          audioElem.src = "https://a.mp3"
          audioElem.play()
      }, false);
      
      // 后面其他地方,可以改变src来直接play
      audioElem.src = "https://b.mp3"
      audioElem.play()
      复制代码
  • iOS 中页面隐藏和显示时,播放audio行为异常

    原因:在某些iOS版本中【iOS12.0.1亲测有坑】,当我们监听页面隐藏和显示事件,在隐藏时调用pause() 暂停,显示时调用play()恢复播放。当 按下home键,页面进入系统后台时 ,pause()正常调用,audio被正常暂停,但是但再次进入页面,显示事件中调用play()就会出现异常了,

    • 第一种异常,如果,我们只是单纯的调用 audioElem.play() ,不会抛出任何错误,但是audio实际却没有真正播放,无任何声音;

    • 第二种异常,如果我们每次在显示事件中执行如下代码中任意一种场景,都会在很多情况下会抛出一个 AbortError 异常,极少数情况才会正常播放。

      //监听页面显示隐藏事件
      addPageVisibilityListener(() => {
          // 隐藏时暂停
        audioElem.pause()
      },() => {
          // 显示时恢复播放
          
          // 重新直接赋值src
          audioElem.src = "https://b.mp3"
          audioElem.play()
          
          // 或者load
          // audioElem.load()
          // audioElem.play()
      })
      复制代码

    解决:这两种异常行为应该都是iOS 12.0.1系统本身的bug。我们可以通过如下2中方式来避免这种两种异常的发生,

    • 方式1, 显示load(),并监听canplaythrough,推荐使用这种方式
    const playAudio = () => {
      audioElem1.removeEventListener('canplaythrough', playAudio)
      let p = audioElem.play()
      if (p !== undefined) {
        p.catch((e) => {
          console.log(e)
        })
      }
    }
    //监听页面显示隐藏事件
    addPageVisibilityListener(() => {
        // 隐藏时暂停
      audioElem.pause()
    },() => {
        // 显示时恢复播放
        audioElem.load()
        audioElem.addEventListener('canplaythrough', playAudio)
    })
    复制代码
    • 方式2,通过setTimeout以及retry来hack避免这种异常发生
    let playAudio = (retry: boolean) => {
        let p = audioElem.play();
        if (p !== undefined) {
            p.catch((e) => {
                if (retry) {
                    setTimeout(() => {
                        playAudio(false);
                    }, 0);
                }
            });
        }
    }
    //监听页面显示隐藏事件
    addPageVisibilityListener(() => {
        // 隐藏时暂停
      audioElem.pause()
    },() => {
        // 显示时恢复播放
        setTimeout(() => {
            playAudio(true)
        }, 500)
    })
    复制代码

以上所述就是小编给大家介绍的《HTML5中Audio使用踩坑汇总》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Hit Refresh

Hit Refresh

Satya Nadella、Greg Shaw / HarperBusiness / 2017-9-26 / USD 20.37

Hit Refresh is about individual change, about the transformation happening inside of Microsoft and the technology that will soon impact all of our lives—the arrival of the most exciting and disruptive......一起来看看 《Hit Refresh》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

MD5 加密
MD5 加密

MD5 加密工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具