欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

网页播放器开发(四)代码精炼提纯

程序员文章站 2022-05-31 09:32:26
四、精简提炼 我们的播放器基本实现了,但是代码复用不高,所以我们要进行封装,以插件的形式体现。 1.插件的基本运行代码如下: 上述代码就是基本的插件代码,下面详细记录这段代码所表示的意思。 前面的分号,可以解决插件与其它js合并时,别的代码可能会产生的错误问题; “(function(){})()” ......

四、精简提炼

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

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