UNPKG

mp3player

Version:

A mp3 player,get data by ajax and play by audiocontext or mediasource.it can download and reprocessing while playing

330 lines (327 loc) 12 kB
function Audio(options) { var emptyFn = function() {}; var onloadstart = null; var onloadedmetadata = null; //更新总时长回调 var ontimeupdate = null; //更新时间回调 var onupdateBar = null; // 更新进度条回调 var onwaiting = null; //开始播放或播放过程中由于缓冲暂停时回调 var onplaying = null; //开始播放或播放过程中缓冲完成回调 var oncanplay = null; //当前位置可播放时回调 var onseeking = null; //寻址中回调 var onplay = null; //播放回调 var onended = null; //播放完成 var onpause = null; //暂停回调 var onerror = null; //错误回调 var audio = null; //音频对象 var playBtn = null; //播放按钮 var pauseBtn = null; //暂停按钮 var forceSeek = false; //是否强制移动播放位置 var currentTime = 0; //当前播放位置 var seekTimeoutId = null; //寻址超时计时器 var seekMaxTime = 30000; //最大寻址时长 var seeking = false; //是否正在寻址中 var canplay = false; //是否就绪 var canUpdateTime = true; //是否可更新时长 var allListener = []; var AudioObj = { init: function(options) { ontimeupdate = options.ontimeupdate || emptyFn; onseeking = options.onseeking || emptyFn; onwaiting = options.onwaiting || emptyFn; onplaying = options.onplaying || emptyFn; oncanplay = options.oncanplay || emptyFn; onloadstart = options.onloadstart || emptyFn; onplay = options.onplay || emptyFn; onended = options.onended || emptyFn; onpause = options.onpause || emptyFn; onupdateBar = options.onupdateBar || emptyFn; onloadedmetadata = options.onloadedmetadata || emptyFn; onerror = options.onerror || emptyFn; audio = options.audio; forceSeek = options.forceSeek || false; playBtn = options.playBtn; pauseBtn = options.pauseBtn; if (!audio) { return; } this.audio = audio; this._bindAudioEvent(); this._bindClickEvent(); }, destroy: function() { this._removeAllEvent(); }, /** * [seek 设置播放位置,对外接口] * @param {Number} percent [百分比或时间] * @param {Boolean} isTime [是否是时间] * @param {Boolean} ifStop [是否停止] */ seek: function(percent, isTime, ifStop) { if (!audio.duration) { if (!isTime) onupdateBar(percent); return; } var self = this; if (!ifStop) { this.play(); } else { this.pause() } if (!isTime) { percent = percent > 100 ? 100 : percent; percent = percent < 0 ? 0 : percent; currentTime = percent * audio.duration / 100; } else { currentTime = percent; percent = currentTime / audio.duration * 100; } if (!this._ifSeekable(currentTime)) { console.log('can not seek'); onupdateBar(percent); return; } ontimeupdate(currentTime); onupdateBar(percent); audio.currentTime = currentTime; if (forceSeek) { seeking = true; self.pause(); seekTimeoutId = setTimeout(function() { seeking = false; self._setNearCurrentTime(); if (!ifStop) { self.play(audio); } }, seekMaxTime); } }, /** * @todo [更新时间,对外接口] * @param {[Number]} percent [百分比或时长] * @param {Boolean} isTime [是否是时长] */ updateTime: function(percent, isTime) { if (isTime) { ontimeupdate(percent); return; } else if (!audio.duration || !audio.seekable.length) { return; } else { percent = percent > 100 ? 100 : percent; percent = percent < 0 ? 0 : percent; ontimeupdate(percent * audio.duration / 100); } }, /** * [play 播放,对外接口] */ play: function() { if (audio.paused) { audio.play(); } }, /** * [play 暂停播放,对外接口] */ pause: function() { if (!audio.paused) { audio.pause(); } }, /** * [stopUpdateTime 停止内部更新时间,对外接口] */ stopUpdateTime: function() { canUpdateTime = false; }, /** * [startUpdateTime 开始内部更新时间,对外接口] */ startUpdateTime: function() { canUpdateTime = true; }, removeAllEvent: function() { this._removeAllEvent(); }, getSource: function() { return audio; }, _bindAudioEvent: function() { var self = this; this._addEvent(audio, 'loadstart', function() { onloadstart(); console.log('loadstart'); }); this._addEvent(audio, 'play', function() { onplay(); console.log('play'); }); this._addEvent(audio, 'ended', function() { onended(); console.log('ended'); }); this._addEvent(audio, 'pause', function() { onpause(); console.log('puase'); }); this._addEvent(audio, 'waiting', function() { onwaiting(); console.log('waiting'); }); this._addEvent(audio, 'playing', function() { if (this.readyState == 4) { onplaying(); } console.log('playing'); }); this._addEvent(audio, 'seeking', function() { onseeking(); console.log('seeking'); }); if (forceSeek) { this._addEvent(audio, 'seeked', function() { if (seeking) { seeking = false; clearTimeout(seekTimeoutId); self.play(); } console.log('seeked'); }); } this._addEvent(audio, 'loadedmetadata', function() { onloadedmetadata(audio.duration); //兼容ios首次必须交互触发canplay事件 oncanplay(); canplay = true; console.log('loadedmetadata'); }) this._addEvent(audio, 'canplay', function() { oncanplay(); canplay = true; console.log('canplay'); }); this._addEvent(audio, 'timeupdate', function() { if (!canUpdateTime) return; var percent = 0; var time = audio.currentTime; if (Math.abs(time - currentTime) > 0.05 && !audio.seeking) { ontimeupdate(time, true); } else { ontimeupdate(time); } if (audio.duration) { percent = time / audio.duration * 100; } onupdateBar(percent); }); this._addEvent(audio, 'stalled', function(e) { console.log('stalled'); }); this._addEvent(audio, 'error', function(e) { onerror(); console.log('error'); }); this._addEvent(audio, 'abort', function(e) { console.log('abort'); }); }, _bindClickEvent: function() { var self = this; if (playBtn) { this._addEvent(playBtn, 'click', function(event) { self._stopPropagation(event); self.play(); }) } if (pauseBtn) { this._addEvent(pauseBtn, 'click', function(event) { self._stopPropagation(event); self.pause(); }) } }, /** * @todo [播放位置是否可寻址] * @param {[Number]} time [时长] * @return {[Boolean]} [结果] */ _ifSeekable: function(time) { var seekable = audio.seekable; var canSeek = false; for (var i = 0; i < seekable.length; i++) { var start = seekable.start(i); var end = seekable.end(i); if (time >= start && time <= end) { canSeek = true; break; } } return canSeek; }, /** * [_setNearCurrentTime 设置最接近的播放位置] */ _setNearCurrentTime: function() { var bufferd = audio.bufferd; var distance = 0; var preTime = 0; for (var i = 0; i < bufferd.length; i++) { var start = bufferd.start(i); var end = bufferd.end(i); if (Math.abs(start - currentTime) < distance || distance == 0) { distance = Math.abs(start - currentTime); preTime = start; } if (Math.abs(end - currentTime) < distance || distance == 0) { distance = Math.abs(end - currentTime); preTime = end; } } currentTime = preTime; audio.currentTime = currentTime; }, _addEvent: function(ele, event_name, func) { if (window.attachEvent) { ele.attachEvent('on' + event_name, func); } else { ele.addEventListener(event_name, func, false); //默认事件是冒泡 } allListener[allListener.length] = { ele: ele, func: func, event: event_name } }, _removeEvent: function(ele, event_name, func) { if (window.attachEvent) { ele.detachEvent('on' + event_name, func); } else { ele.removeEventListener(event_name, func, false); } }, _removeAllEvent: function() { for (var i = 0; i < allListener.length; i++) { var listener = allListener[i]; this._removeEvent(listener.ele, listener.event, listener.func); } }, _stopPropagation: function(e) { if (e && e.stopPropagation) { //非IE e.stopPropagation(); } else { //IE window.event.cancelBubble = true; } }, _preventDefault: function(e) { e.preventDefault ? e.preventDefault() : (e.returnValue = false); } } if (options) { AudioObj.init(options); } return AudioObj; } export default Audio;