1. 程式人生 > >網頁播放器開發(四)代碼精煉提純

網頁播放器開發(四)代碼精煉提純

css 調用 合並 def box 運算 詳細 mime remove

四、精簡提煉

我們的播放器基本實現了,但是代碼復用不高,所以我們要進行封裝,以插件的形式體現。

1.插件的基本運行代碼如下:

;(function(undefined){

‘use strict‘;

... ...

})()

上述代碼就是基本的插件代碼,下面詳細記錄這段代碼所表示的意思。

前面的分號,可以解決插件與其它js合並時,別的代碼可能會產生的錯誤問題;

“(function(){})()”這個結構表示立即執行第一個括號內的函數,其實立即執行函數還有另一種寫法,“(function(){}())”。看大家的喜好,我一般喜歡用第一種;

“undefined”做為參數傳入,因為在老一輩的瀏覽器是不被支持的,直接使用會報錯,js框架要考慮到兼容性,因此增加一個形參undefined,就算有人把外面的 undefined 定義了,插件裏面的 undefined 依然不受影響。

嚴格模式開發

下面進一步補充代碼函數內代碼:

‘use strict‘;

這行代碼表示嚴格模式,顧名思義,嚴格模式就是使得 Javascript 在更嚴格的條件下運行,有助於我們更規範的開發。如果在語法檢測時發現語法問題,則整個代碼塊失效,並導致一個語法異常。如果在運行期出現了違反嚴格模式的代碼,則拋出執行異常。

定義我們的播放器插件“playMythology”

下面我們真正開始我們的插件代碼了,目前整個代碼如下:

;(function(undefined){
‘use strict‘;
var _global;
function playMythology(opt) {
... ...
}
playMythology.prototype = {};
//將插件對象暴露給全局對象
_global = (function() {
return this || (0, eval)(‘this‘);
}());
if (typeof module !== "undefined" && module.exports) {
module.exports 
= playMythology; } else if (typeof define === "function" && define.amd) { define(function() { return playMythology; }); } else { !(‘playMythology‘ in _global) && (_global.playMythology = playMythology); } })()

定義“_global”,並把全局環境賦值給一個_global。

並把當前頂級對象賦值給這個變量,代碼如下:

_global = (function() {
return this || (0, eval)(‘this‘);
}());

看這段代碼又是個立即執行函數,不是上面提到的第一種的立即執行函數,而是這種第二種立即執行函數:(functiong(){})結構;首先先介紹一下eval()函數的作用:eval() 函數計算 JavaScript 字符串,並把它作為腳本代碼來執行如果參數是一個表達式,eval() 函數將執行表達式如果參數是Javascript語句,eval()將執行 Javascript 語句。然後在逐一分析語句:return this表示返回當前對象;第一個括號內的逗號操作符 對它的每個操作數求值(從左到右),並返回最後一個操作數的值那麽這個(0, eval)(‘this‘)相當於evalthis’),那麽為什麽不用evalthis’),而用(0, eval)(‘this‘)呢?在嚴格模式下,如果沒有給 this指定值的話,它就是未定義的為了防止在嚴格模式下window變量被賦予undefined,使用(0, eval)(this)就可以把this重新指向全局環境對象因為(0, eval)(this)通過逗號表達式對它的操作數執行了GetValue,計算出一個值this的值指向了全局對象eval(‘this’)計算出的是一個引用是一個直接調用,方法中的this值是obj的引用

定義“playMythology”,表示我們插件的名稱。然後我們給這個函數添加屬性,通過prototype來添加,簡單解釋一下prototype是函數的一個屬性,並且是函數的原型對象。prototype只能夠被函數調用

為了實現插件的模塊化並且讓我們的插件也是一個模塊,就得讓我們的插件也實現模塊化的機制。要判斷是否存在加載器,如果存在加載器,我們就使用加載器,如果不存在加載器。我們就使用頂級域對象。下面代碼就實現了這個功能

if (typeof module !== "undefined" && module.exports) {

module.exports = playMythology;

} else if (typeof define === "function" && define.amd) {

define(function() {

return playMythology;

});

} else {

!(‘playMythology‘ in _global) && (_global.playMythology = playMythology);

}

介紹一下主要的運算符:==”表示相等;“===表示絕對相等“!=”表示不相等;“!==”,表示嚴格不相等JavaScript中,unllundefined並不相同但是null==undefined為真,null===undefined為假,所以null !== undefined 為真。

“typeof ”表示返回數據類型,2種使用方式:typeof(表達式)typeof 變量名,第一種是對表達式做運算,第二種是對變量做運算。回類型為字符串,值包括如下幾種:

1. ‘undefined‘ --未定義的變量或值

2. ‘boolean‘ --布爾類型的變量或值

3. ‘string‘ --字符串類型的變量或值

4. ‘number‘ --數字類型的變量或值

5. ‘object‘ --對象類型的變量或值,或者null(這個是js歷史遺留問題,將null作為object類型處理)

6. ‘function‘ --函數類型的變量或值module.exports 對象是由模塊系統創建的。在我們自己寫模塊的時候,需要在模塊最後寫好模塊接口,聲明這個模塊對外暴露什麽內容,module.exports 提供了暴露接口的方法。這種方法可以返回全局共享的變量或者方法。

介紹一下AMD,AMD是一種規範就是其中比較著名一個,全稱是Asynchronous Module Definition,即異步模塊加載機制。從它的規範描述頁面看,AMD很短也很簡單,但它卻完整描述了模塊的定義,依賴關系,引用關系以及加載機制。感興趣的朋友可以認真研究一下,requireJSNodeJsDojoJQuery全部在使用,可見它的價值

2.基本函數

引入CSS文件函數:前端開發引入CSS文件是必不可少的,css主要功能是對頁面布局進行美化,我希望開發的插件的皮膚可以動態設置,所以要動態引入CSS文件,定義了引入CSS文件函數,具體代碼如下:

//path表示引入CSS文件路徑

function cssinto(path) {

//如果CSS文件錯誤,拋出錯誤異常

if (!path || path.length === 0) {

throw new Error(‘argument "path" is required !‘);

}

//獲取head 對象

var head = document.getElementsByTagName(‘head‘)[0];

//創建link標簽並插入到head標簽內

var link = document.createElement(‘link‘);

link.href = path;

link.rel = ‘stylesheet‘;

link.type = ‘text/css‘;

head.appendChild(link);

}

時間轉換函數:主要功能,講audio currentTime 時間戳轉換轉化成“分:秒”顯示格式。

//path表示引入CSS文件路徑

//時間顯示轉換

function conversion(value) {

let minute = Math.floor(value / 60)

minute = minute.toString().length === 1 ? (‘0‘ + minute) : minute

let second = Math.round(value % 60)

second = second.toString().length === 1 ? (‘0‘ + second) : second

return minute+":"+second

}

引入json文件函數:file值json文件路徑,callback只得是回調函數,當文件加載完畢就調用該函數。

function readTextFile(file, callback) {

var rawFile = new XMLHttpRequest();

rawFile.overrideMimeType("application/json");

rawFile.open("GET", file, true);

rawFile.onreadystatechange = function() {

if (rawFile.readyState === 4 && rawFile.status == "200") {

callback(rawFile.responseText);

}

}

rawFile.send(null);

}

getElementsByClass因為我們未講window傳入插件,所以有些方法我們是不能使用的,所以我們定義下面方法實現,通過class查找html中dom對象。

//判斷插件是否存在“getElementsByClass”,沒存在,將使用下面方法實現該功能。

if (!(‘getElementsByClass‘ in HTMLElement)) {

//prototype在前面已經提到過了,通過“prototype”給HTMLElement添加屬性方法“getElementsByClass”

HTMLElement.prototype.getElementsByClass = function(n) {

var el = [],

_el = this.getElementsByTagName(‘*‘);

for (var i = 0; i < _el.length; i++) {

if (!!_el[i].className && (typeof _el[i].className == ‘string‘) && _el[i].className.indexOf(n) > -1) {

el[el.length] = _el[i];

}

}

return el;

};

((typeof HTMLDocument !== ‘undefined‘) ? HTMLDocument : Document).prototype.getElementsByClass = HTMLElement.prototype

.getElementsByClass;

}

參數合並函數: 對象合並,這個主要用於插件默認參數賦值操作,如果設置就使用新的參數,如果不設置就使用默認參數

//表示原有參數,n表示新參數,override表示是否進行覆蓋

function extend(o, n, override) {

for (var key in n) {

if (n.hasOwnProperty(key) && (!o.hasOwnProperty(key) || override)) {

o[key] = n[key];

}

}

return o;

}

3.基本功能

參數初始化,裏面有詳細的註釋。

_initial: function(opt) {

// 默認參數

var def = {

skinID: "default", //默認皮膚路徑

domID: "musicbox" //設置播放器容器ID

};

//如果函數初始化時,設置參數時,進行合並,如果沒有設置使用默認參數

this.def = extend(def, opt, true);

//用於JSON文件存儲數據

this.data = {};

//播放器初始音量為0.3

this.sound = 0.3;

this.currentID = 0;

//創建audion

this.audion = document.createElement("AUDIO");

//獲取播放器dom對象

this.dom = document.getElementById(def.domID);

//播放器初始音量

this.audion.volume = this.sound;

//定義定時器,用於進度條調整,歌曲滾動等功能

this.timecolick;

//歌曲容器

this.songBox;

//播放器狀態,0表示順序播放;1表示循環播放;2表示隨機播放。

this.isPlayState = 0;

//歌曲列表用於存儲歌曲數據

this.songList;

//歌曲播放進度條

this.songProgress;

//播放進度條上的播放頭

this.songPlayHead;

//判斷歌曲是否允許滾動,0表示允許,1表示不允許

this.isSlide = 0;

//播放進度,0表示初始位置

this.playprogress = 0;

//最大

this.playMax = 0;

//播放器是否在播放,0表示正在播放,1表示暫停

this.isPlaying = 0;

//歌曲列表滾動距離

this.scollHeight = 20;

//初始化播放器,並開始播放

this._GetData();

},

播放器界面初始化,並播放歌曲

//設置播放器界面

var _this = this;//把當前對象存到_this

//初始化CSS文件

cssinto("skin/" + this.def.skinID + "/css/music.css");

//讀取json數據

readTextFile("skin/" + this.def.skinID + "/data.json", function(text) {

//數據讀取到data

_this.data = JSON.parse(text);

//把界面HTML代碼插入容器,界面初始化

_this.dom.innerHTML = _this.data[0].MusicHtml;

//設置歌曲列表

var htmlinsert = "";

//過去歌曲容器dom對象

_this.songBox = _this.dom.getElementsByClass(_this.data[0].musiclistbox)[0];

//存儲歌曲數據

_this.songList = _this.data[0].Songlist;

for (var i = 0; i < _this.songList.length; i++) {

htmlinsert += ‘<li><span>‘ + _this.songList[i].songname + ‘</span></li>‘;

}

_this.songBox.innerHTML = htmlinsert;

//設置音樂列表單擊事件

for (var i = 0; i < _this.songBox.childNodes.length; i++) {

(

function(j) {

_this.songBox.childNodes[j].onclick = function() {

_this._PlaySong(j);

}

})(i)

}

//所有數據加載完畢,開始播放歌曲

_this._PlaySong(0);

暫停播放功能。

//播放停止按鈕事件 _this.dom.getElementsByClass(_this.data[0].playBT)[0].onclick = function(e) {

//如果正在播放則停止播放

if (_this.isPlaying == 0) {

this.className = "playbutton";

_this.isPlaying = 1;

_this.audion.pause()

} else //如果停止播放則開始播放

{

this.className = "pausebutton";

_this.isPlaying = 0;

_this.audion.play();

}

}

歌曲切換功能,上一首,下一首切換。

//上一首按鈕

_this.dom.getElementsByClass(_this.data[0].preBton)[0].onclick = function(e) {

if (_this.currentID > 0) {

_this.currentID--;

} else {

_this.currentID = _this.songList.length - 1;

}

_this._PlaySong(_this.currentID)

}

//下一首按鈕

_this.dom.getElementsByClass(_this.data[0].nextBton)[0].onclick = function(e) {

if (_this.currentID < _this.songList.length - 1) {

_this.currentID++;

} else {

_this.currentID = 0;

}

_this._PlaySong(_this.currentID)

}

隨機播放功能,按鈕點擊後歌曲將實現隨機播放。

//隨機播放按鈕

var randombtn = _this.dom.getElementsByClass(_this.data[0].randombtn)[0];

randombtn.onclick = function(e) {

if (_this.isPlayState == 1) {

_this.isPlayState = 0;

this.className = _this.data[0].shuffle;

return;

}

if (_this.isPlayState == 2) {

onereplay.className = _this.data[0].replay;

}

_this.isPlayState = 1;

this.className = _this.data[0].shuffleon;

}

單曲循環功能,按鈕點擊後歌曲將實現單曲循環播放。

//單曲循環按鈕

var onereplay = _this.dom.getElementsByClass(_this.data[0].onereplay)[0];

onereplay.onclick = function(e) {

if (_this.isPlayState == 2) {

_this.isPlayState = 0;

this.className = _this.data[0].replay;

return;

}

if (_this.isPlayState == 1) {

randombtn.className = _this.data[0].shuffleon;

}

_this.isPlayState = 2;

this.className =  _this.data[0].replay;

 

}

音量調節功能,拖放調節音量功能。

//音量調節按鈕

var soundHead = _this.dom.getElementsByClass(_this.data[0].soundHead)[0];

var soundBox = _this.dom.getElementsByClass(_this.data[0].soundBox)[0];

var soundCurrentTime = _this.dom.getElementsByClass(_this.data[0].soundCurrentTime)[0];

soundHead.style.left = _this.sound * 100 + ‘px‘;

soundCurrentTime.style.width = _this.sound * 100 + ‘%‘;

soundHead.onmousedown = function(e) {

var x = (e || window.event).clientX;

var l = this.offsetLeft;

var max = soundBox.offsetWidth - this.offsetWidth;

document.onmousemove = function(e) {

var thisX = (e || window.event).clientX;

var to = Math.min(max, Math.max(-2, l + (thisX - x)));

if (to < 0) {

to = 0;

}

soundHead.style.left = to + ‘px‘;

//此句代碼可以除去選中效果

window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();

_this.audion.volume = to / max;

//document.querySelector(‘.now‘)

soundCurrentTime.style.width = to / max * 100 + ‘%‘;

}

//註意此處是document 才能有好的拖動效果

document.onmouseup = function() {

document.onmousemove = null;

};

}

進度條功能。

//獲取進度條dom

_this.songProgress = _this.dom.getElementsByClass(_this.data[0].SongProgress)[0];

//獲取進度條上的播放頭

_this.songPlayHead = _this.dom.getElementsByClass(_this.data[0].playHead)[0];

//單擊進度條 調整發播放進度

_this.songProgress.onclick = function(e) {

var x = (e || window.event).clientX;

var left = x - this.offsetLeft - _this.songPlayHead.offsetWidth;

var maxwidth = _this.songProgress.offsetWidth;

_this.dom.getElementsByClass(_this.data[0].playHead)[0].style.left = left + ‘px‘;

var currenttime = _this.audion.duration * (left / maxwidth)

var p = left / maxwidth

_this.audion.currentTime = p * _this.audion.duration;

_this.audion.play();

};

//拖動播放頭,調整播放進度

_this.songPlayHead.onmousedown = function(e) {

var x = (e || window.event).clientX;

var l = this.offsetLeft;

var max = _this.songProgress.offsetWidth - this.offsetWidth;

_this.playMax = max;

document.onmousemove = function(e) {

var thisX = (e || window.event).clientX;

var to = Math.min(max, Math.max(-2, l + (thisX - x)));

if (to < 0) {

to = 0;

}

_this.playprogress = to;

_this.isSlide = 1;

_this.songPlayHead.style.left = to + ‘px‘;

_this.dom.getElementsByClass(_this.data[0].SingerCurrentTime)[0].innerHTML = conversion(_this.audion.duration * (_this

.playprogress / _this.playMax));

//此句代碼可以除去選中效果

window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();

// _this.audion.currentTime = to / max;

 

}

//註意此處是document 才能有好的拖動效果

document.onmouseup = function() {

_this.isSlide = 0;

_this.audion.currentTime = (_this.playprogress / _this.playMax) * _this.audion.duration;

_this.audion.play();

document.onmousemove = null;

};

定時函數功能

//定時函數

_this.timecolick = setInterval(function() {

if (_this.isSlide == 1) {

return;

}

//設置進度條

var percent = Math.floor(_this.audion.currentTime / _this.audion.duration * 10000) / 100 + "%";

_this.songPlayHead.style.left = percent;

//設置當前播放時間

_this.dom.getElementsByClass(_this.data[0].SingerCurrentTime)[0].innerHTML = conversion(_this.audion.currentTime);

if (_this.audion.ended) {

if (_this.isPlayState == 0) //順序播放

{

if (_this.currentID < _this.songList.length - 1) {

_this.currentID++;

} else {

_this.currentID = 0;

}

} else if (_this.isPlayState == 1) //隨機播放

{

_this.currentID = Math.floor(Math.random() * _this.songList.length - 1)

} else //單曲循環

{

_this.currentID = _this.currentID;

}

console.log(_this.currentID)

_this._PlaySong(_this.currentID);

}

}, 100)

歌曲播放功能

__PlaySong: function(songID) {

var _this = this;

this.audion.setAttribute("src", this.data[0].Songlist[songID].songurl);

this.dom.getElementsByClass(this.data[0].SongName)[0].innerHTML = _this.data[0].Songlist[songID].songname;

_this.dom.getElementsByClass(this.data[0].Singer)[0].innerHTML = this.data[0].Songlist[songID].songer;

_this.audion.onloadedmetadata = function() {

_this.dom.getElementsByClass(this.data[0].SingerCurrentTime)[0].innerHTML = conversion(_this.audion.currentTime);

_this.dom.getElementsByClass(this.data[0].showalltime)[0].innerHTML = conversion(_this.audion.duration)

}

this.audion.play();

var Songlist = _this.songBox.childNodes;

for (var i = 0; i < Songlist.length; i++) {

if (songID == i) {

Songlist.item(i).setAttribute("class", this.data[0].currentSong);

} else {

Songlist.item(i).setAttribute("class", "")

}

}

//console.log(_this.scollHeight*songID)

_this._scollToMusiclist(songID, _this.dom.getElementsByClass(this.data[0].MusicList)[0])

 

}

歌曲滾動功能。

_scollToMusiclist: function(singID, wmusicbox) {

//ok  2019年4月5日,終於調試成功,長時間不開發真的不行,好多事情想不到,剛才不停的滾動現象是由於我沒有對最大值進行判斷,如果超過最大值,我們需要把最大值賦給變量,那樣就不會不停的閃爍了。

var gundong = singID * 20;

var maxgundong = wmusicbox.scrollHeight - wmusicbox.offsetHeight;

if (gundong > maxgundong) {

gundong = maxgundong;

}

var scollTime = setInterval(function() {

console.log(wmusicbox.scrollTop)

if (wmusicbox.scrollTop < gundong) {

wmusicbox.scrollTop = wmusicbox.scrollTop + 1;

console.log(gundong)

} else if (wmusicbox.scrollTop > gundong) {

wmusicbox.scrollTop = wmusicbox.scrollTop - 1;

console.log("2")

} else {

console.log("=")

clearInterval(scollTime);

}

})

}

ok,這網我們的網頁播放器已經全部編寫完畢,我把源代碼打包,提供大家下載,多提寶貴意見。 單擊下載

網頁播放器開發(四)代碼精煉提純