H5全局播放器实现,暂不支持部分缓冲播放

H5全局全功能播放器实现,暂不支持仅缓冲部分播放。 此播放器功能支持播放区间,支持淡入淡出、播放控制, seek控制、支持个性化配置。

H5Audio方法介绍

arduino 复制代码
// HTML <audio> 元素支持一系列事件,这些事件可以帮助您管理音频的播放、暂停、加载等状态
// onloadstart: 当浏览器开始加载音频时触发。
// onloadedmetadata: 当浏览器已加载音频的元数据时触发。
// onloadeddata: 当浏览器已加载音频的全部数据时触发。
// oncanplay: 当浏览器可以开始播放音频时触发。
// oncanplaythrough: 当浏览器预计可以在不停顿的情况下播放音频时触发。
// onplay: 当音频开始播放时触发。
// onplaying: 当音频正在播放时触发。
// onpause: 当音频暂停时触发。
// onended: 当音频播放结束时触发。
// onerror: 当音频加载出错时触发。
// onprogress: 当音频正在下载时触发,以便显示下载进度。
// ontimeupdate: 当音频播放位置发生变化时触发,以便更新播放进度条等。


// HTML <audio> 元素本身没有方法,但可以通过JavaScript来操作它。以下是一些常用的通过 JavaScript 操作 <audio> 元素的方法:

// play(): 开始播放音频。
// pause(): 暂停音频播放。
// load(): 重新加载音频。
// canPlayType(type): 返回一个字符串,指示浏览器是否能够播放指定类型的音频文件。
// currentTime: 属性,用于获取或设置音频的当前播放位置。
// volume: 属性,用于获取或设置音频的音量。
// muted: 属性,用于获取或设置音频是否静音。
// duration: 属性,返回音频的总时长。
// seekable: 属性,返回一个 TimeRanges 对象,表示音频可寻址的时间范围。
// ended: 属性,返回一个布尔值,指示音频是否已经播放结束。

实现

kotlin 复制代码
    
const globleConfig = {
  volume: 1,
  analyserFftSize : 2048,
  mapType: 'url' , // 当存在重复url时推荐给url添加模块后缀的方式避免渲染重复
}
const defaultConfig = {
   endedToStart: true, // 播放结束后是否回归零位置
   loop: false, // 是否循环播放
   fadeInTime: 0, // 淡入时间 暂没支持
   fadeOutTime: 0, // 淡出时间 暂没支持
   volumeGain: 0, // 音量偏移,当前歌曲音量降低或增加0-1,音量最大为1
   analyser: false, // 创建频谱直方图
   canSeekNotCurrent: false, // 当不是当前播放时是否可以调整下次播放位置
   autoToStartIfNotCurrent: true, // 当不是当前时是否自动归零
}


export class GlobalAudioPlayer {

    constructor() {
        if (GlobalAudioPlayer.instance) {
            return GlobalAudioPlayer.instance;
        }

        this.initAudio();

        GlobalAudioPlayer.instance = this;
    }

    initAudio() {
        this.audio = new Audio();
        this.globleAudioAnalyser = null;
        this.players = {}; // 存储每个URL对应的播放数据
        this.lastUrl = null; // 记录上一个播放的URL
        this.currentUrl = null // 记录当前正在播放的URL
        this.currentPlayer = null; // 记录当前播放器
        this.changeUrlListener=[];
        this.globalListenerMap= {
            'playStateChange':[],
            'curPlayEnded':[],
            'curPlayUrlChange':[],
        };
        this.audio.crossOrigin = 'anonymous';
        this.checkNetworkStatus();
        this.audio.addEventListener('loadstart', (...args) => {
            if(!this.currentUrl){
                return;
            }
            // console.log('loadstart', ...args);
            this.triggerListenerBroadcast(this.players[this.currentUrl], 'onloadstart', ...args)
            this.triggerListenerBroadcast(this.players[this.currentUrl], 'onLoadingStateChange', true)
        });
        this.audio.addEventListener('loadedmetadata', (...args) => {
            if(!this.currentUrl){
                return;
            }
            // console.log('loadedmetadata', ...args);
            this.triggerListenerBroadcast(this.players[this.currentUrl], 'onloadedmetadata', ...args)
            if(this.audio.duration!=Infinity){
                this.players[this.currentUrl].duration = this.audio.duration;
            }
            this.triggerListenerBroadcast(this.players[this.currentUrl], 'onDurationChange', this.getDuration(this.players[this.currentUrl]))
        });
        this.audio.addEventListener('loadeddata', (...args) => {
            if(!this.currentUrl){
                return;
            }
            // console.log('loadeddata', ...args);
            if(this.audio.duration!=Infinity){
                this.players[this.currentUrl].duration = this.audio.duration;
            }
            this.triggerListenerBroadcast(this.players[this.currentUrl], 'onloadeddata')
            this.triggerListenerBroadcast(this.players[this.currentUrl], 'onDurationChange', this.getDuration(this.players[this.currentUrl]))
            this.triggerListenerBroadcast(this.players[this.currentUrl], 'onLoadingStateChange', false)
        });
        this.audio.addEventListener('canplay', (...args) => {
            if(!this.currentUrl){
                return;
            }
            // console.log('canplay', ...args);
            this.triggerListenerBroadcast(this.players[this.currentUrl], 'oncanplay', ...args)
            this.triggerListenerBroadcast(this.players[this.currentUrl], 'onLoadingStateChange', false)
        });
        this.audio.addEventListener('canplaythrough', (...args) => {
            if(!this.currentUrl){
                return;
            }
            if(!this.currentPlayer.isPlaying){
               this.pause(this.currentUrl);
            }
            // console.log('canplaythrough', ...args);
            this.triggerListenerBroadcast(this.players[this.currentUrl], 'oncanplaythrough', ...args)
            this.triggerListenerBroadcast(this.players[this.currentUrl], 'onLoadingStateChange', false)
        });
        
        this.audio.addEventListener('play', (...args) => {
            if(!this.currentUrl){
                return;
            }
            // console.log('play', ...args);
            this.triggerListenerBroadcast(this.players[this.currentUrl], 'onplay', ...args)
            // this.triggerListenerBroadcast(this.players[this.currentUrl], 'onPlayStateChange', true)
            this.triggerListenerBroadcast(this.players[this.currentUrl], 'onLoadingStateChange', false)
        });
        this.audio.addEventListener('playing', (...args) => {
            if(!this.currentUrl){
                return;
            }
            this.currentPlayer.isPlaying = true;
            // console.log('playing', ...args);
            this.triggerListenerBroadcast(this.players[this.currentUrl], 'oncanplaythrough', ...args)
            this.triggerListenerBroadcast(this.players[this.currentUrl], 'onPlayStateChange', true)
            this.triggerListenerBroadcast(this.players[this.currentUrl], 'onLoadingStateChange', false)
        });
        this.audio.addEventListener('pause', (...args) => {
            if(!this.currentUrl){
                return;
            }
            this.currentPlayer.isPlaying = false;
            // console.log('pause', ...args);
            this.triggerListenerBroadcast(this.players[this.currentUrl], 'onpause', ...args)
            this.triggerListenerBroadcast(this.players[this.currentUrl], 'onPlayStateChange', false)
        });
        this.audio.addEventListener('ended', (...args) => {
            if(!this.currentUrl){
                return;
            }
            // console.log('ended', ...args);
            this.triggerListenerBroadcast(this.players[this.currentUrl], 'onended', ...args)
            this.triggerListenerBroadcast(this.players[this.currentUrl], 'onPlayStateChange', false)
            const { endedToStart, loop, fadeInTime, fadeOutTime  } = this.players[this.currentUrl].personalizedConfig;
            if (endedToStart) {
                this.audio.currentTime = 0;
            }
            if (loop) {
                this.paly(this.currentUrl);
            }else{
                this.pause(this.currentUrl);
            }
            this.triggerGlobleListenerBroadcast('curPlayEnded',{
                currentUrl:this.currentUrl,
                player:this.players[this.currentUrl]
            })
        });
        this.audio.addEventListener('error', (...args) => {
            console.error('addEventListener__error', ...args);
            if(!this.currentUrl){
                return;
            }
            
            this.triggerListenerBroadcast(this.players[this.currentUrl], 'onerror', ...args)
        });
        this.audio.addEventListener('progress', (...args) => {
            if(!this.currentUrl){
                return;
            }
            // console.log('progress', ...args);
            this.triggerListenerBroadcast(this.players[this.currentUrl], 'onprogress')
            if (this.audio.readyState >= 2) {
                if((this.audio.buffered.end(0) / this.audio.duration) == (this.audio.currentTime / this.audio.duration)){
                    this.triggerListenerBroadcast(this.players[this.currentUrl], 'onLoadingStateChange', true)
                }
            }
        });
        this.audio.addEventListener('timeupdate', (...args) => {
            // console.log('timeupdate', ...args);
            if(!this.currentUrl){
                return;
            }
            this.triggerListenerBroadcast(this.players[this.currentUrl], 'ontimeupdate', ...args)
            this.triggerListenerBroadcast(this.players[this.currentUrl], 'onLoadingStateChange', false)

            const { endedToStart, loop, fadeInTime, fadeOutTime, volumeGain  } = this.players[this.currentUrl].personalizedConfig;
            let {clipStart, clipEnd } = this.checkClipTime(this.players[this.currentUrl]);
            if(clipStart>=0 && clipEnd > clipStart){
                let currentTime = this.audio.currentTime;
                if(currentTime < clipStart || currentTime >= clipEnd){
                    if(currentTime < clipStart){
                        this.audio.currentTime = clipStart;
                    }else{
                        if (endedToStart) {
                            this.audio.currentTime = clipStart;
                        }else{
                            this.audio.currentTime = clipEnd;
                        }
                        if (loop) {
                            this.paly(this.currentUrl);
                        }else{
                            this.pause(this.currentUrl);
                            
                        }
                        this.triggerGlobleListenerBroadcast('curPlayEnded',{
                            currentUrl:this.currentUrl,
                            player:this.players[this.currentUrl]
                        })
                    }
                }
            }else {
                clipEnd = this.audio.duration
            }
            let volume = globleConfig.volume + volumeGain;
            if(volume < 0){
                volume = 0;
            }else if(volume > 1){
                volume = 1;
            }
            if(fadeInTime > 0 && this.audio.currentTime <= clipStart + fadeInTime){
                this.audio.volume = (this.audio.currentTime - clipStart) / fadeInTime * volume;
            }else if(fadeOutTime > 0 && this.audio.currentTime >= clipEnd-fadeOutTime){
                this.audio.volume = (clipEnd - this.audio.currentTime) / fadeOutTime * volume;
            }else{
                this.audio.volume = volume;
            }
            this.players[this.currentUrl].currentTime = this.audio.currentTime;
            this.triggerListenerBroadcast(this.players[this.currentUrl], 'onPositionChange', this.getPositionData(this.players[this.currentUrl]));
            if(this.players[this.currentUrl].personalizedConfig.analyser){
                this.triggerListenerBroadcast(this.players[this.currentUrl], 'onAudioAnalyserChange', this.getAnalyserData(this.players[this.currentUrl]));
            }
            if(!this.currentPlayer.isPlaying){
                this.pause(this.currentUrl);
            }
        });
    }
    
    getUuid(){
        return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, c =>
        (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
        );
    }

    splitArray(arr, count){
        const size = Math.ceil(arr.length ?? 0) / count;
        return arr.length
            ? arr.reduce(
                (res, cur) => (
                res[res.length - 1].length < size
                    ? res[res.length - 1].push(cur)
                    : res.push([cur]),
                res
                ),
                [[]]
            )
        : [];
    }

    formatTime(secs) {
        if(isNaN(Number(secs))){
            return '00:00';
        }
        secs = Math.ceil(secs);
        const minutes = Math.floor(secs / 60) || 0;
        const seconds = Math.floor(secs - minutes * 60) || 0;
        return `${String(minutes).padStart(2,0)}:${String(seconds).padStart(2,0)}`;
    }

    canPlayType(type){
        return this.audio.canPlayType(type);
    }

    setVolume(volume){
        globleConfig.volume = volume;
        this.audio.volume = volume;
    }

    checkClipTime(player){
        let {clipStart, clipEnd, duration} = player;
        clipStart = clipStart>0 ? clipStart > duration ? 0 : clipStart : 0;
        clipEnd = clipEnd >0 ? clipEnd > duration ? duration : clipEnd : 0;
        return {
            clipStart,
            clipEnd
        }
    }

    getDuration(player){
        const {clipStart, clipEnd } = this.checkClipTime(player);
        const { duration } =  player;
        if(clipStart >= 0 && clipEnd > clipStart){
            return {
                duration: clipEnd - clipStart,
                durationTime: this.formatTime(clipEnd - clipStart),
                sourceDuration: duration,
                sourceDurationTime: this.formatTime(duration)

            }
        }else{
            return {
                duration: duration,
                durationTime: this.formatTime(duration),
                sourceDuration: duration,
                sourceDurationTime: this.formatTime(duration)
            }
        }
    }
    

    getPositionData(player){
        const {clipStart, clipEnd } = this.checkClipTime(player);
        const {currentTime, duration} = player;
        if(clipStart>=0 && clipEnd > clipStart){
            return {
                start: 0,
                startTime: this.formatTime(0), 
                end: (clipEnd-clipStart),
                endTime: this.formatTime(clipEnd-clipStart),
                current: currentTime-clipStart,
                currentTime: this.formatTime(currentTime-clipStart),
                progress:  (currentTime-clipStart)/(clipEnd-clipStart),

                sourceStart: 0,
                sourceStartTime: this.formatTime(0),
                sourceEnd: duration,
                sourceEndTime: this.formatTime(duration),
                sourceCurrent: currentTime,
                sourceCurrentTime: this.formatTime(currentTime),
                sourceProgress: currentTime/duration,
            }
        }else{
            return {
                start: 0,
                startTime: this.formatTime(0), 
                end: duration,
                endTime: this.formatTime(duration),
                current: currentTime,
                currentTime: this.formatTime(currentTime),
                progress:  currentTime/duration,

                sourceStart: 0,
                sourceStartTime: this.formatTime(0), 
                sourceEnd: duration,
                sourceEndTime: this.formatTime(duration),
                sourceCurrent: currentTime,
                sourceCurrent: this.formatTime(currentTime),
                sourceProgress:  currentTime/duration,
            }
        }
       
    }

    // registChangeUrlListener(listener){
    //     if(!this.changeUrlListener.includes(listener)){
    //         this.changeUrlListener.push(listener);
    //     }
    // }
    // releaseChangeUrlListener(listener){
    //     const index = this.changeUrlListener.indexOf(listener);
    //     if(index>-1){
    //         this.changeUrlListener.splice(index, 1);
    //     }
    // }

    registGlobleListener(eventName,listener){
        // this.globalListenerMap= {
        //     'playStateChange':[],
        //     'curPlayEnded':[],
        //     'curPlayUrlChange':[],
        // }
        const listeners = this.globalListenerMap[eventName];;
        if(listeners && !listeners.includes(listener)){
            listeners.push(listener);
        }
    }
    releaseGlobleListener(eventName,listener){
        const listeners = this.globalListenerMap[eventName];
        const index = listeners?.indexOf(listener);
        if(index>-1){
            listeners.splice(index, 1);
        }
    }
   
    triggerGlobleListenerBroadcast(eventName, ...args){
        const listeners = this.globalListenerMap[eventName];
        if(listeners){
            listeners.forEach((listener)=>{
                listener(...args);
            })
        }
    }

    triggerListenerBroadcast(player, listenerName, ...args){
        player && player.listeners.forEach((listener)=>{
            listener[listenerName]&&listener[listenerName](...args);
        })
        if(listenerName=="onPlayStateChange"){
            this.triggerGlobleListenerBroadcast('playStateChange', {
                isPlaying: args[0],
                lastUrl: this.lastUrl,
                player: player,
                currentUrl: this.currentUrl
            })
        }
    }

    setupAudioContext = () => {
        try {
          if (typeof AudioContext !== 'undefined') {
            return new AudioContext();
          } else if (typeof webkitAudioContext !== 'undefined') {
            return new webkitAudioContext();
          } else {
            return null;
          }
        } catch(e) {
          return null;
        }
      }

    
    openAudioAnalyser= (player)=> {
        let isAnalyser = player.personalizedConfig?.analyser;
        if(isAnalyser){
            if(!this.globleAudioAnalyser){
                let audioContext = this.setupAudioContext();
                try{
                    audioContext.resume().then(() => {
                       let analyser = audioContext.createAnalyser();
                        analyser.fftSize = globleConfig.analyserFftSize;
                        let analyserMediaElementSource = audioContext.createMediaElementSource(this.audio);
                        analyserMediaElementSource.connect(analyser);
                        analyserMediaElementSource.connect(audioContext.destination);
                        this.globleAudioAnalyser = {
                            audioContext,
                            analyser,
                            analyserMediaElementSource
                        }
                        player.audioAnalyser = {
                            ...player.audioAnalyser,
                            ...this.globleAudioAnalyser,
                            active: true,
                        }
                    });
                    
                }catch(e){
                    console.log("initAudioAnalyser__error", e);
                }
            } else {
                // this.globleAudioAnalyser.audioContext.resume()
                player.audioAnalyser = {
                    ...player.audioAnalyser,
                    ...this.globleAudioAnalyser,
                    active: true,
                }
            }
        }
    }

    closeAudioAnalyser= async (player, isDestroy = false)=> {
        let isAnalyser = player.personalizedConfig?.analyser;
        let { audioContext, analyser, analyserMediaElementSource} = player.audioAnalyser;
        if(isAnalyser && audioContext){
            player.audioAnalyser.active = false;
            // this.globleAudioAnalyser.audioContext.suspend();
            if(!isDestroy){
                this.triggerListenerBroadcast(
                    player,
                    'onAudioAnalyserChange',
                    this.getAnalyserData(player, true)
                )
            }
        }
    }

    getAnalyserData(player){
        let isAnalyser = player.personalizedConfig?.analyser;
        if(!isAnalyser){
            return null
        }
        let { audioContext, analyser, analyserMediaElementSource, active} = player.audioAnalyser;
        if(this.globleAudioAnalyser && active){
            return {
                player,
                getAnalyserData:(count, averageCount=2)=>{
                    let length=analyser.frequencyBinCount * 44100 / audioContext.sampleRate|0;
  		            let arr = new Uint8Array(length);
                    analyser.getByteFrequencyData(arr);
                    let l0 = 0;
                    let r0 = 0;
                    for(let i=0; i<arr.length; i++){
                        if(arr[i]>0 && l0==0){
                            l0 = i;
                        }
                        if(arr[length-i-1]>0 && r0==0){
                            r0 = length-i-1
                        }
                        if(l0!=0 && r0!=0){
                            break;
                        }
                    }
                    arr =  arr.slice(l0,r0);
                    return this.splitArray(arr, count).map((chuck, index) => {
                        const step = Math.floor((chuck.length ?? 0) / averageCount);
                        let sum = 0;
                        for (let i = 0; i < chuck.length; i += step) {
                            sum += Math.abs(chuck[i]);
                        }
                        const average = sum / (chuck.length / step);
                        return Math.round(average);
                    });
                }
            }
        }else{
            return {
                player,
                getAnalyserData:(count)=>{
                    return new Uint8Array(count);
                }
            }
        }
    }

    getOrCreatePlayer(url, options) {
        if(!options && this.players[url]){
            return {
                url,
                player: this.players[url],
                id: this.players[url].id
            }
        }
        const {
            duration = 0,
            clipStart = 0,
            clipEnd = 0,
            listeners,
            personalizedConfig = {}
        } = options;
        const { 
            // 推荐
            // onStateChange=()=>{},
            onDurationChange=()=>{},
            onBuffer=()=>{},
            onLoadingStateChange=()=>{},
            onPlayStateChange=()=>{},
            onPositionChange=()=>{},
            onAudioAnalyserChange=()=>{},

            // 非必要 原生透传
            onloadstart=()=>{},
            onloadedmetadata=()=>{},
            onloadeddata=()=>{},
            oncanplay=()=>{},
            oncanplaythrough=()=>{},
            onplay=()=>{},
            onplaying=()=>{},
            onpause=()=>{},
            onended=()=>{},
            onerror=()=>{},
            onprogress=()=>{},
            ontimeupdate=()=>{},
        } = listeners;
        const id = this.getUuid();
        const listenersCache = {
            id,
            // onStateChange,
            onDurationChange,
            onBuffer,
            onLoadingStateChange,
            onPlayStateChange,
            onPositionChange,
            onAudioAnalyserChange,

            onloadstart,
            onloadedmetadata,
            onloadeddata,
            oncanplay,
            oncanplaythrough,
            onplay,
            onplaying,
            onpause,
            onended,
            onerror,
            onprogress,
            ontimeupdate,
        };
        
        if (!this.players[url]) {
            this.players[url]= {
                id: id,
                url: url,
                isPlaying: false,
                bufferedPosition: 0,
                duration: duration,
                currentTime: 0,
                listeners: [listenersCache],
                clipStart: clipStart,
                clipEnd: clipEnd,
                audioAnalyser: {
                    audioContext: null,
                    analyser: null,
                    fftSize: 2048,
                    analyserMediaElementSource: null,
                },
                personalizedConfig:{
                    ...defaultConfig,
                    ...personalizedConfig
                }
            };
            const _clipStart = this.checkClipTime(this.players[url]).clipStart;
            this.players[url].currentTime= _clipStart
            this.triggerListenerBroadcast(
                this.players[url],
                 'onPositionChange',
                 this.getPositionData(this.players[url])
            )
        }else{
            this.players[url].listeners.push(listenersCache);
            this.players[url].personalizedConfig={
                ...defaultConfig,
                ...this.players[url].personalizedConfig,
                ...personalizedConfig
            }
            this.triggerListenerBroadcast(
                this.players[url],
                'onPositionChange',
                this.getPositionData(this.players[url])
            )
        }
        return {
            player: this.players[url],
            id,
            listenerId: id,
            url
        }
    }

    registerPlayUrl(url, options) { // 注册播放器
        return this.getOrCreatePlayer(url, options);
    }

    releasePlayer(obj) { // 组件销毁时调用释放
        const { player, uuid, url} = obj;
        if (this.currentUrl == url) {
            this.audio.pause();
            this.audio.url = null;
            this.currentUrl = null;
            this.closeAudioAnalyser(this.players[url], true);
            this.currentPlayer = null;
        }
        if(this.lastUrl==url){
            this.lastUrl = null;
        }
        delete this.players[url];
    }

    setClip(url, from, to){
        const player = this.players[url];
        if(from >= 0 && to > from){
            player.clipStart = from;
            player.clipEnd = to;
        }
        const { clipStart } = this.checkClipTime(player)
        player.currentTime = clipStart;
        if(this.currentUrl === url){
            this.audio.currentTime = clipStart;
        }else{
            this.triggerListenerBroadcast(
                player,
               'onPositionChange',
               this.getPositionData(player)
            )
        }
        
    }

    checkNetworkStatus() {
        window.addEventListener('online', ()=>{
            if(this.audio.src){
                this.audio.load();
                if(this.players[this.audio.src]?.isPlaying){
                    this.audio.play();
                }
            }
        });
    }

    async play(url, isInitSyncState=true) {
        // console.log('play', url)

        if (this.currentUrl !== url) {
            this.lastUrl = this.currentUrl;
            this.pause(this.currentUrl);
            this.currentUrl = url;
            this.audio.src = url;
            if(navigator.onLine){
              this.audio.load();
            }
            this.audio.currentTime = this.players[url].currentTime;
            if(this.currentPlayer){
                const { clipStart } = this.checkClipTime(this.currentPlayer)
                const { canSeekNotCurrent, autoToStartIfNotCurrent } = this.currentPlayer.personalizedConfig;
                if (autoToStartIfNotCurrent) {
                    this.currentPlayer.currentTime = clipStart;
                    this.triggerListenerBroadcast(
                        this.players[this.lastUrl],
                        'onPositionChange',
                        this.getPositionData(this.players[this.lastUrl])
                    )
                }
                await this.closeAudioAnalyser(this.currentPlayer);
            }
            
            this.currentPlayer = this.players[url];   
        }
        this.audio.currentTime = this.currentPlayer.currentTime;
        this.triggerGlobleListenerBroadcast('curPlayUrlChange', url, this.lastUrl)
        try{
            await this.audio.play();
            this.currentPlayer.isPlaying = true;
            this.openAudioAnalyser(this.currentPlayer);
            if(isInitSyncState){
                this.triggerListenerBroadcast(this.currentPlayer, 'onplay')
                this.triggerListenerBroadcast(this.currentPlayer, 'onPlayStateChange', true)
            }
        }catch(e){
            console.log("player Error",e);
            this.audio.pause();
            this.currentPlayer.isPlaying = false;
            if(isInitSyncState){
                this.triggerListenerBroadcast(this.currentPlayer, 'onpause')
                this.triggerListenerBroadcast(this.currentPlayer, 'onPlayStateChange', false)
            }
        }
        return this.currentPlayer.isPlaying;

    }

    pause(url, toStart) {
        if (this.currentUrl == url  && url!='') {
            if (this.currentPlayer && this.currentPlayer.isPlaying) {
                this.currentPlayer.isPlaying = false;
                this.audio.pause();
                if(toStart){
                    this.seek(url, 0);
                }
                
                this.triggerListenerBroadcast(this.currentPlayer, 'onpause')
                this.triggerListenerBroadcast(this.currentPlayer, 'onPlayStateChange', false)
            }
        }else{
          if(toStart){
             this.seek(url, 0);
          }
        }
    }

    seek(url, progress) {
        if(!url){
            return;
        }
        if (this.currentUrl == url) {
            const player =this.players[this.currentUrl];
            const {clipStart, clipEnd } = this.checkClipTime(player);
            const {duration} = player;
            if(clipStart>=0 && clipEnd > clipStart){
                const time = clipStart + progress * (clipEnd - clipStart);
                this.audio.currentTime = time;
            }else{
                this.audio.currentTime = progress * duration;
            }
        }else{
            const player = this.players[url];
            const { canSeekNotCurrent, autoToStartIfNotCurrent }  = player.personalizedConfig
            const {clipStart, clipEnd } = this.checkClipTime(player);
            const {duration} = player;
            let time = duration * progress;
            if(clipStart>=0 && clipEnd > clipStart){
                time = clipStart + progress * (clipEnd - clipStart);
            }
            if(canSeekNotCurrent){
                player.currentTime = time;
            }else{
                player.currentTime = clipStart;
            }
            this.triggerListenerBroadcast(
                player,
                'onPositionChange',
                this.getPositionData(player,)
            )
        }
    }
}


const globalAudioPlayer = new GlobalAudioPlayer();

export default globalAudioPlayer;

调用示例

arduino 复制代码
// Example usage: 单位秒

// const renderData = reactive({
//     url: props.url,
//     startTime: '00:00',
//     endTime: '00:00',
//     bufferTime: '00:00',
//     duration: props.duration,
//     progress: 0,
//     isPlaying: false,
//     isLoading: false,
//   });
// 初始化
// const curPlayerData = globalAudioPlayer.registerPlayUrl(renderData.url, {
//     duration: props.duration ?? 0, 
//     clipStart: props.from, 
//     clipEnd: props.to, 
//     listeners: {
//       onDurationChange({duration, durationTime,sourceDuration,sourceDurationTime}){
//         renderData.duration = duration;
//         renderData.endTime = durationTime;
//       },
//       onPlayStateChange(isPlaying){
//         renderData.isPlaying = isPlaying;
//       },
//       onLoadingStateChange(isLoading){
//         renderData.isLoading = isLoading;
//       },
//       onPositionChange: (obj)=>{
//         const {progress, currentTime, endTime} = obj;
//         if(!stateData.isChangeProgress){
//           renderData.startTime = currentTime;
//           renderData.progress = progress*100;
//         }
//       },
//     }
//   });
// 播放
// globalAudioPlayer.play(renderData.url, true);
// 暂停
// globalAudioPlayer.pause(renderData.url);
// 调整播放位置
// globalAudioPlayer.seek(renderData.url, renderData.progress/100);
// 调整播放区间
// globalAudioPlayer.setClip(renderData.url, startTime, endTime);
// 释放
// globalAudioPlayer.releasePlayer(curPlayerData);
相关推荐
非著名架构师12 分钟前
js混淆的方式方法
开发语言·javascript·ecmascript
多多米10051 小时前
初学Vue(2)
前端·javascript·vue.js
敏编程1 小时前
网页前端开发之Javascript入门篇(5/9):函数
开发语言·javascript
看到请催我学习1 小时前
内存缓存和硬盘缓存
开发语言·前端·javascript·vue.js·缓存·ecmascript
XiaoYu20023 小时前
22.JS高级-ES6之Symbol类型与Set、Map数据结构
前端·javascript·代码规范
儒雅的烤地瓜3 小时前
JS | JS中判断数组的6种方法,你知道几个?
javascript·instanceof·判断数组·数组方法·isarray·isprototypeof
道爷我悟了3 小时前
Vue入门-指令学习-v-on
javascript·vue.js·学习
27669582923 小时前
京东e卡滑块 分析
java·javascript·python·node.js·go·滑块·京东
PleaSure乐事3 小时前
【Node.js】内置模块FileSystem的保姆级入门讲解
javascript·node.js·es6·filesystem
雷特IT4 小时前
Uncaught TypeError: 0 is not a function的解决方法
前端·javascript