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

相信神話發表於2019-04-20

四、精簡提煉

我們的播放器基本實現了,但是程式碼複用不高,所以我們要進行封裝,以外掛的形式體現。

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,這網我們的網頁播放器已經全部編寫完畢,我把原始碼打包,提供大家下載,多提寶貴意見。 單擊下載

 

相關文章