zoukankan      html  css  js  c++  java
  • 网页播放器开发(四)代码精炼提纯

    四、精简提炼

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

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

  • 相关阅读:
    [Tips] kubeconfig合并
    某书补充题选做
    Card
    Ant Design Pro V5 使用 Cesium 地图开发包遇到加载widgets.css样式报错
    工作相关
    pc端谷歌浏览器长截图
    dao层与xml之间联系
    三周的大学生活,我到底是怎么过来的
    湖南大学推荐书《社会学大纲》阅读有感 其二
    湖南大学新生报到游记 其一
  • 原文地址:https://www.cnblogs.com/agoodlife/p/10741432.html
Copyright © 2011-2022 走看看