由于工作需求,原生的html5播放器雖然好用,但是ui不是太好看(個人覺得還可以)但是過不了設計的眼光,所以需要創建一個好看的播放器組件。一個組件的好處用面向對象的方式來講就相當于一個類,你可以重復的去新建這個類,生成重復的組件對象。在這里,相當于你可以在同一個頁面用new的方式,方便的創建多個自定義的播放器。
html5播放器原理:其實無非還是使用了html5的audio元素,然后基于audio的api自己去寫播放器各個方法(播放、暫停、進度條、聲音控制)等等,我看了下網易的云音樂的播放器,也是采用的audio的API來控制的。
原生audio控件:
網易云音樂皮膚:
筆者的audio皮膚(雖然挺一般的,但是跟公司的網站色系比較統一)
首先一個js插件關鍵的要點就是 不依賴、不污染、獨立,不依賴與其它的方法和元素,獨立自成一體。
多基于jquery的插件會依賴于jquery,我也寫過一些jquery的插件,但是這次要寫的是完全基于原生js的插件。
IIFE:immediately invoked functional expression 就是我們平常所說的立即執行函數:
(function() {
}());
然后我們需要一個構造函數來實現一個類,關于如何用js實現類可以參考一些js的書籍,這里我們使用構造函數的方式。在立即執行函數中的作用域是全局 window, 函數中的this 就代表的是 window ,如下先定義this.AudioPlayer 就等于window.AudioPlayer , 這樣相當于AudioPlayer這個變量名稱已經在全局作用域中使用了。
(function() { this.AudioPlayer = function AudioPlayer() {
}
}());
然后建立一個對象就簡單了,直接 var _audioPlayer = new AudioPlayer()就創建了一個對象。
一般來講,一個對象總會有一些初始化參數,比如我們的播放器AudioPlayer就可以有 音樂的url參數,是否需要音量控制的參數等等。 我們在構造函數的內部需要創建一個缺省的參數對象defaults,然后還需要有一個傳入的參數和缺省的參數相互匹配的一個方法。
(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>
結果如下:
本站文章版權歸原作者及原出處所有 。內容為作者個人觀點, 并不代表本站贊同其觀點和對其真實性負責,本站只提供參考并不構成任何投資及應用建議。本站是一個個人學習交流的平臺,網站上部分文章為轉載,并不用于任何商業目的,我們已經盡可能的對作者和來源進行了通告,但是能力有限或疏忽,造成漏登,請及時聯系我們,我們將根據著作權人的要求,立即更正或者刪除有關內容。本站擁有對此聲明的最終解釋權。