因爲工做需求,原生的html5播放器雖然好用,可是ui不是太好看(我的以爲還能夠)可是過不了設計的眼光,因此須要建立一個好看的播放器組件。一個組件的好處用面向對象的方式來說就至關於一個類,你能夠重複的去新建這個類,生成重複的組件對象。在這裏,至關於你能夠在同一個頁面用new的方式,方便的建立多個自定義的播放器。javascript
html5播放器原理:其實無非仍是使用了html5的audio元素,而後基於audio的api本身去寫播放器各個方法(播放、暫停、進度條、聲音控制)等等,我看了下網易的雲音樂的播放器,也是採用的audio的API來控制的。css
原生audio控件:
html
網易雲音樂皮膚:
html5
筆者的audio皮膚(雖然挺通常的,可是跟公司的網站色系比較統一)
java
首先一個js插件關鍵的要點就是 不依賴、不污染、獨立,不依賴與其它的方法和元素,獨立自成一體。
最多基於jquery的插件會依賴於jquery,我也寫過一些jquery的插件,可是此次要寫的是徹底基於原生js的插件。jquery
IIFE:immediately invoked functional expression 就是咱們日常所說的當即執行函數:ios
(function() { }());
而後咱們須要一個構造函數來實現一個類,關於如何用js實現類能夠參考一些js的書籍,這裏咱們使用構造函數的方式。在當即執行函數中的做用域是全局 window, 函數中的this 就表明的是 window ,以下先定義this.AudioPlayer 就等於window.AudioPlayer , 這樣至關於AudioPlayer這個變量名稱已經在全局做用域中使用了。git
(function() { this.AudioPlayer = function AudioPlayer() { } }());
而後創建一個對象就簡單了,直接 var _audioPlayer = new AudioPlayer()就建立了一個對象。github
通常來說,一個對象總會有一些初始化參數,好比咱們的播放器AudioPlayer就能夠有 音樂的url參數,是否須要音量控制的參數等等。 咱們在構造函數的內部須要建立一個缺省的參數對象defaults,而後還須要有一個傳入的參數和缺省的參數相互匹配的一個方法。express
(function(){ this.AudioPlayer = function AudioPlayer() { var config; //定義缺省參數 var defaults = { audiodom:'', audiosrc:'', showVolume:true } //傳入的參數於缺省參數的對應 if (arguments[0] && typeof arguments[0] === "object") { config = extendDefaults(defaults, arguments[0]); } } function extendDefaults(source, properties) { var property; for(property in properties) { if (properties.hasOwnProperty(property)) { source[property] = properties[property]; } } return source; } })();
公有方法的做用很明顯,能夠建立這個對象的一些在外部須要調用的功能,如: 我在這裏簡單的寫了一個當播放器切換音樂來源url的時候的一個公有方法,由於當須要換音樂url的時候,不須要再去從新建立一個播放器對象了。
AudioPlayer.prototype.setAudioSrc = function (src) { this.audioPlayer.setAttribute('src',src); }
整合在一塊兒寫就是這樣:
(function(){ this.AudioPlayer = function AudioPlayer() { var config; //定義缺省參數 var defaults = { audiodom:'', audiosrc:'', showVolume:true } //傳入的參數於缺省參數的對應 if (arguments[0] && typeof arguments[0] === "object") { config = extendDefaults(defaults, arguments[0]); } } //公有方法 AudioPlayer.prototype.setAudioSrc = function (src) { this.audioPlayer.setAttribute('src',src); } function extendDefaults(source, properties) { var property; for(property in properties) { if (properties.hasOwnProperty(property)) { source[property] = properties[property]; } } return source; } })();
總結:這樣基本上寫一個簡單js組件的框架就搭建起來了,在裏面不斷的加入方法和功能就能夠了。
如下就是一個播放器的js代碼,css部分我就不貼出代碼來了,附上github 的地址:
https://github.com/tangolivesky/audioplayer
(function () { this.AudioPlayer = function AudioPlayer() { var config; // Define option defaults var defaults = { audiodom:'', audiosrc:'', showVolume:true } // Create options by extending defaults with the passed in arugments if (arguments[0] && typeof arguments[0] === "object") { config = extendDefaults(defaults, arguments[0]); } var duration; var myAudioPlayer = document.createElement("div"); var audioPlayer = document.createElement("audio"); var playButton = document.createElement("button"); var timeLine = document.createElement("div"); var timeProgressBar = document.createElement("span"); var playhead = document.createElement("div"); var currentTime = document.createElement("div"); var volumeLine = document.createElement("div"); var volumeLineBar = document.createElement("span"); var volumeLineHead = document.createElement("div"); var volumeHorn = document.createElement('span'); var playheadSpan = document.createElement('span'); var timelineWidth = 230; var volumelinewidth = 50; var onplayhead = false; var volumeStatus = []; this.myAudioPlayer = myAudioPlayer; this.playhead = playhead; this.timeProgressBar = timeProgressBar; this.timelineWidth = timelineWidth; this.playButton = playButton; this.currentTime = currentTime; this.transTime = transTime; this.intialStatus = intialStatus; this.playStatus = playStatus; this.volumeHorn = volumeHorn; this.audioPlayer = audioPlayer; myAudioPlayer.className = "audioplayer"; playButton.className = "playbutton play"; timeLine.className = "timeline"; timeProgressBar.className = "time-progress-bar"; playhead.className = "playhead intial"; playheadSpan.className = 'round'; currentTime.className = "current-time"; audioPlayer.setAttribute('controls', 'controls'); audioPlayer.style.display = "none"; volumeLine.className = "audio-line"; volumeLineHead.className = "audio-line-head"; volumeLineBar.className = "audio-line-bar"; volumeHorn.className = 'horn full'; if(config.audiodom === ''){ document.body.appendChild(myAudioPlayer); }else{ document.getElementById(config.audiodom).appendChild(myAudioPlayer); } myAudioPlayer.appendChild(playButton); playhead.appendChild(playheadSpan); timeLine.appendChild(timeProgressBar); timeLine.appendChild(playhead); myAudioPlayer.appendChild(timeLine); myAudioPlayer.appendChild(currentTime); myAudioPlayer.appendChild(audioPlayer); volumeLine.appendChild(volumeLineBar); volumeLine.appendChild(volumeLineHead); myAudioPlayer.appendChild(volumeHorn); myAudioPlayer.appendChild(volumeLine); if (config.hasOwnProperty("audiosrc")) { audioPlayer.setAttribute('src', config.audiosrc); } else{ playButton.setAttribute("disabled", "disabled"); timeLine.setAttribute("disabled", "disabled"); volumeLine.setAttribute("disabled", "disabled"); playhead.setAttribute("disabled", "disabled"); volumeLineHead.setAttribute("disabled", "disabled"); volumeHorn.setAttribute("disabled", "disabled"); } if(config.hasOwnProperty("showVolume")){ if(!config.showVolume){ volumeLine.style.display='block'; volumeLineHead.style.display='block'; volumeLineBar.style.display='block'; myAudioPlayer.style.width ='430px'; } } audioPlayer.addEventListener('loadedmetadata',function(){ currentTime.innerHTML = transTime(parseInt(audioPlayer.duration)); },false); playButton.addEventListener('click', play, false); audioPlayer.addEventListener('timeupdate', timeUpdate, false); audioPlayer.addEventListener('canplaythrough', function () { duration = audioPlayer.duration; }, false); timeLine.addEventListener('click', function (event) { moveplayhead(event); audioPlayer.currentTime = duration * clickPercent(event); }, false); playhead.addEventListener('mousedown', mouseDown, false); window.addEventListener('mouseup', mouseUp, false); volumeLine.addEventListener('click', function (event) { movevolumehead(event); audioPlayer.volume = volumeClickPercent(event); if (audioPlayer.volume == 0) { volumeHorn.className = 'horn'; }else if(audioPlayer.volume>=0.8){ volumeHorn.className = 'horn full'; }else if(audioPlayer.volume>=0.5){ volumeHorn.className = 'horn two'; }else if(audioPlayer.volume>0){ volumeHorn.className = 'horn one'; } }, false); //volumeLineHead.addEventListener('mousedown', volumeMouseDown, false); //window.addEventListener('mouseup', volumeMouseUp, false); //聲音按鈕控制 volumeHorn.addEventListener('click',function(event){ if (audioPlayer.volume > 0) { volumeStatus[0] = audioPlayer.volume; volumeStatus[1] = volumeHorn.className; volumeStatus[2] = volumeLineBar.style.width; volumeStatus[3] = volumeLineHead.style.marginLeft; volumeHorn.className = 'horn'; movevolumehead(event); audioPlayer.volume = volumeClickPercent(event); }else{ audioPlayer.volume = volumeStatus[0]; volumeHorn.className = volumeStatus[1]; volumeLineBar.style.width = volumeStatus[2]; volumeLineHead.style.marginLeft = volumeStatus[3]; } },false); //樣式調整 //設置成初始狀態 function intialStatus(){ playhead.className = 'playhead intial'; } //設置成播放狀態 function playStatus(){ playhead.className = 'playhead'; } function mouseDown() { //樣式調整 playStatus(); onplayhead = true; window.addEventListener('mousemove', moveplayhead, true); audioPlayer.removeEventListener('timeupdate', timeUpdate, false); } function mouseUp(e) { //樣式調整 if (parseInt(playhead.style.marginLeft) <= 0) { intialStatus(); } if (onplayhead == true) { moveplayhead(e); window.removeEventListener('mousemove', moveplayhead, true); // change current time audioPlayer.currentTime = duration * clickPercent(e); audioPlayer.addEventListener('timeupdate', timeUpdate, false); } onplayhead = false; } function clickPercent(e) { var timelineleft = getOffsetLeft(timeLine); return (e.pageX - timelineleft) / timelineWidth; } function volumeClickPercent(e) { var volume = 0; var volumeLineLeft = getOffsetLeft(volumeLine); if ((e.pageX - volumeLineLeft) / volumelinewidth < 0)volume = 0; if ((e.pageX - volumeLineLeft) / volumelinewidth > 1)volume = 1; if ((e.pageX - volumeLineLeft) / volumelinewidth >= 0 && (e.pageX - volumeLineLeft) / volumelinewidth <= 1)volume = (e.pageX - volumeLineLeft) / volumelinewidth; return volume; } function moveplayhead(e) { var timelineleft = getOffsetLeft(timeLine); var newMargLeft = e.pageX - timelineleft; if (newMargLeft >= 0 && newMargLeft <= timelineWidth) { playhead.style.marginLeft = newMargLeft + "px"; timeProgressBar.style.width = newMargLeft * 100 / timelineWidth + "%"; } if (newMargLeft < 0) { playhead.style.marginLeft = "0px"; timeProgressBar.style.width = "0%"; } if (newMargLeft > timelineWidth) { playhead.style.marginLeft = timelineWidth + "px"; timeProgressBar.style.width = "100%"; } } function movevolumehead(e) { var volumeLineLeft = getOffsetLeft(volumeLine); var newMargLeft = e.pageX - volumeLineLeft; if (newMargLeft >= 0 && newMargLeft <= volumelinewidth) { volumeLineHead.style.marginLeft = newMargLeft-10 + "px"; volumeLineBar.style.width = newMargLeft * 100 / volumelinewidth + "%"; } if (newMargLeft < 0) { volumeLineHead.style.marginLeft = "-10px"; volumeLineBar.style.width = "0%"; } if (newMargLeft > timelineWidth) { volumeLineHead.style.marginLeft = volumelinewidth-10 + "px"; volumeLineBar.style.width = "100%"; } } function play() { //樣式控制 playStatus(); // start music if (audioPlayer.paused) { audioPlayer.play(); // remove play, add pause playButton.className = ""; playButton.className = "playbutton pause"; } else { // pause music audioPlayer.pause(); // remove pause, add play playButton.className = ""; playButton.className = "playbutton play"; } } function timeUpdate() { var playPercent = audioPlayer.currentTime / duration; var playPercentWidth = timelineWidth * playPercent; var playTime = transTime(parseInt(audioPlayer.currentTime)); currentTime.innerHTML = playTime; playhead.style.marginLeft = playPercentWidth + "px"; timeProgressBar.style.width = playPercent * 100 + "%"; if (audioPlayer.currentTime == duration) { playButton.className = ""; playButton.className = "playbutton play"; } } function transTime(time){ if (time<10) { return('00:0'+time); }else if(time<60){ return('00:'+time); }else if(time<600){ var i = parseInt(time/60); var j = parseInt(time%60); if (j<10) { return('0'+i+':0'+j); }else{ return('0'+i+':'+j); } }else if(time<6000){ var i = parseInt(time/60); var j = parseInt(time%60); if (j<10) { return(i+':0'+j); }else{ return(i+':'+j); } }else{ return time; } } function volumeMouseDown() { onplayhead = true; window.addEventListener('mousemove', movevolumehead, true); //audioPlayer.removeEventListener('timeupdate', timeUpdate, false); } function volumeMouseUp(e) { if (onplayhead == true) { movevolumehead(e); window.removeEventListener('mousemove', movevolumehead, true); audioPlayer.volume = volumeClickPercent(e); } onplayhead = false; } function getOffsetLeft( elem ) { var offsetLeft = 0; do { if ( !isNaN( elem.offsetLeft ) ) { offsetLeft += elem.offsetLeft; } } while( elem = elem.offsetParent ); return offsetLeft; } } AudioPlayer.prototype.getAudioPlayer = function () { return this.myAudioPlayer; }; AudioPlayer.prototype.setAudioSrc = function (src) { this.audioPlayer.setAttribute('src',src); } // Utility method to extend defaults with user options function extendDefaults(source, properties) { var property; for (property in properties) { if (properties.hasOwnProperty(property)) { source[property] = properties[property]; } } return source; } })();
在html調用就比較簡單了,只須要一點點的代碼,甚至不須要了解audio標籤,就能夠生成兩個互不衝突的音樂播放器。
<div> <div id="audio1"> </div> <div id="audio2"> </div> </div> <script type="text/javascript" src="audioplayer.js"></script> <script type="text/javascript"> var audioPlayer = new AudioPlayer({ audiodom:"audio1", audiosrc:"http://www.alexkatz.me/codepen/music/interlude.mp3", }); var audioPlayer2 = new AudioPlayer({ audiodom:"audio2", audiosrc:"http://www.alexkatz.me/codepen/music/interlude.mp3", }); </script>
結果以下: