封装一个播放音频的组件

252 阅读2分钟

1. 我需要在一个页面中播放一段音频

  • 使用audio标签即可,里面提供了播放、暂停等功能
class BGAudio extends PureComponent{
    constructor(props)
    super(props)
    this.state = {
        audioUrl = ''
    }
    
    // 播放方法
    play = (url) => {
        const { audioUrl } = this.state;
        if (url !== audioUrl) {
            this.pause();
            this.setState({
                audioUrl: url,
            }, () => {
                this.audioRef?.play()?.catch((err) => {console.log(err);});
            });
        } else {
          this.audioRef?.play()?.catch((err) => {console.log(err);});
        }
    }
    
    // 暂停方法
    pause = () => {
    }
    
    // 当媒介已就绪可以开始播放时运行的脚本
    onPlay = () => {
    }
    
    // 当媒介已到达结尾时运行的脚本(可发送类似“感谢观看”之类的消息)
    onPlayEnd = () => {
    }
    
    render(){
        audioUrl 
        ? (
            <audio
              ref={(ref) => { if (ref) this.audioRef = ref; }}
              onPlay={this.onPlay}
              onEnded={onPlayEnd}
              src={audioSrc}
              preload="auto"
              autoPlay
            >
        )
    } 
}

2. 我需要在这个页面的子组件中播放多个音频

在多次使用音频组件时就需要考虑到组件的复用、性能等因素:

  • 在多个组件中就没必要重复引用该组件,因为多一个audio标签就意味着多一个dom节点
  • 音频的如果需要预加载,则需统一在一个组件内加载请求
  • 可以写一个管理音频播放的单例方法,里面存放声音的播放、预加载、阻断播放等方法供其他子组件在需要时调用
import type BGAudio from '../BGAudio';

export default class AudioBGManager{
  private static instance: AudioBGManager;
  
  private audioRef: BGAudio

  // 获取单例实例
  static getInstance(): AudioBGManager {
    if (AudioBGManager.instance) return AudioBGManager.instance;
    AudioBGManager.instance = new AudioBGManager();
    return AudioBGManager.instance;
  }
  
  // 已经播放过的音频路径
  hasBeenPlayedAudioSUrlArr = [];
  
  // 音频锁,在需要阻塞音频的时候add一个key进入该list,size大于0时,阻塞音频的播放
  private blockAudioList = new Set([])
  
  // 播放音频
  playBGAudio = (src) => {
      // 如果此时音频锁长度大于0、音频已经播放过等 可以阻止音频播放
  }
  
  onPlay = (evnet) => {
    const audioEle = event.target
    this.hasBeenPlayedAudioSUrlArr.push(audioEle?.src);
  };
  
  // 初始化挂载ref
  audioRefInit = () => {
      this.audioRef = audioRef
  }
  
  // 预加载音频
  preLoadAudioUrl = () => {}
}

<BGAudio onPlay={AudioBGManager.getInstance().onPlay} ref={AudioBGManager.getInstance().audioRefInit} />

3. preload和autoplay(摘自MDN)

preload

枚举属性,让开发者自行思考来示意浏览器使用何种加载方式以达到最好的用户体验。可以是以下属性之一:

  • none: 示意用户可能不会播放该音频,或者服务器希望节省带宽;换句话说,该音频不会被缓存;
  • metadata: 示意即使用户可能不会播放该音频,但获取元数据 (例如音频长度) 还是有必要的。
  • auto: 示意用户可能会播放音频;换句话说,如果有必要,整个音频都将被加载,即使用户不期望使用。

autoplay 属性的优先级高于 preload。如果 autoplay 被指定,浏览器将显式地开始下载媒体以供播放

autoplay

  • 声明该属性,音频会尽快自动播放,不会等待整个音频文件下载完成