四、精简提炼

我们的播放器基本实现了,但是代码复用不高,所以我们要进行封装,以插件的形式体现。

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')相当于eval(‘this’),那么为什么不用eval(‘
this’),而用(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中,unll与
undefined并不相同,但是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很短也很简单,但它却完整描述了模块的定义,依赖关系,引用关系以及加载机制。感兴趣的朋友可以认真研究一下,
requireJS,NodeJs,Dojo,JQuery全部在使用,可见它的价值。

 

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,这网我们的网页播放器已经全部编写完毕,我把源代码打包,提供大家下载,多提宝贵意见。 单击下载 <http://agoodlife.cn/down/>

 

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:[email protected]
QQ群:637538335
关注微信