4. Refining
Our player is basically implemented, but the code reuse is not high, so we want to encapsulate it as a plug-in.
1. The basic running code of the plug-in is as follows:
;(function(undefined){ 'use strict'; ... ... })()
The above code is the basic plug-in code. The meaning of this code is detailed below.
The previous semicolon can solve the problem that other codes may cause errors when the plug-in is merged with other js.
The structure'(function(){} ()'means that the function in the first parenthesis is executed immediately. In fact, there is another way to execute the function immediately,'(function(){} ()'.Looking at your preferences, I usually like to use the first one.
"Undefined" is passed in as a parameter because older browsers are not supported, direct use can cause errors, and the js framework considers compatibility, so adding a parameter undefined will not affect undefined inside the plug-in even if someone has defined it outside.
Strict Mode Development
Here is a further complement to the code within the function:
'use strict';
This line of code represents a strict pattern, which, as the name implies, allows Javascript to run under stricter conditions, helping us develop more regularly.If a grammar problem is found during grammar detection, the entire block of code becomes invalid and results in a grammar exception.Execution exceptions are thrown if code violates strict mode occurs during runtime.
Define our player plug-in "playMythology"
Let's really start with our plug-in code. For now, the whole code is as follows:
;(function(undefined){ 'use strict'; var _global; function playMythology(opt) { ... ... } playMythology.prototype = {}; //Exposing plug-in objects to global objects _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); } })()
Define "_global" and assign a global environment to a _global.
And assign the current top-level object to this variable, code as follows:
_global = (function() { return this || (0, eval)('this'); }());
Look at this code as an immediate execution function, not the first one mentioned above, but the second one: (functiong(){}) structure; first, the function eval() is introduced: eval() calculates a JavaScript string and executes it as script code. If the parameter is an expression, the eval() function will be persistentLine expression, eval() executes a Javascript statement if the parameter is a Javascript statement.Then parse the statement one by one: return this means to return the current object; the comma operator in the first parenthesis evaluates each of its operands (from left to right) and returns the value of the last one, so this (0, eval)('this') is equivalent to Eval ('this'), so why not use Eval ('this') instead of (0, eval)('this')?In strict mode, if no value is specified for this, it is undefined. To prevent the window variable from being assigned undefined in strict mode, use (0, eval)('this') to re-point this to the global environment object.Because (0, eval)('this') executes GetValue on its operand through a comma expression, calculates a value so that the value of this points to a global object; Eval ('this') calculates a reference, a direct call, and the value of this in the method is a reference to obj.
Define "playMythology" to indicate the name of our plug-in.Then let's add properties to this function, and add them through prototype, which simply explains that prototype is a property of a function and is the prototype object of a function.Prototype can only be called by a function.
To modularize plug-ins and make our plug-ins a module, we have to make our plug-ins modular as well.This simply determines if there is a loader, and if there is one, we use the loader, if there is no loader.We use top-level domain objects.The following code does 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); }
Introduces the main operators:'=='means equality;'===' means absolute equality;'!='means inequality;'!==' means strict inequality.In JavaScript, unll is not the same as undefined, but null==undefined is true, null===undefined is false, so null!== undefined is true.
"Typeof" denotes the return data type and can be used in two ways: typeof (expression) and the name of a typeof variable. The first is to operate on an expression, and the second is to operate on a variable.The return type is a string and the values include the following:
1.'undefined'--- Undefined variable or value
2.'boolean'--- Variables or values of Boolean type
3.'string'-- - Variables or values of type string
4.'number'-- Variables or values of type number
5.'object'-- -- variable or value of object type, or null (this is a legacy of js history and treats null as object type)
6.'function'-- Variables of type function or value module.exports objects are created by the module system.When we write the module ourselves, we need to write the module interface at the end of the module, declare what the module exposes to the outside world, and module.exports provides a way to expose the interface.This method can return globally shared variables or methods.
Introducing AMD,AMD is one of the well-known specifications, the full name is Asynchronous Module Definition, which is the asynchronous module loading mechanism.From its specification description page, AMD is short and simple, but it fully describes the module's definition, dependencies, reference relationships, and loading mechanism.Interested friends can study carefully, requireJS, NodeJs, Dojo, JQuery are all in use, we can see its value.
2. Basic Functions
Introduction of CSS file functions: front-end development is necessary to introduce CSS files, the main function of CSS is to beautify the page layout, I want to develop plug-in skin can be set dynamically, so to dynamically introduce CSS files, defined the introduction of CSS file functions, the specific code is as follows:
//path Representation introduction CSS File Path function cssinto(path) { //If CSS File error, throw error exception if (!path || path.length === 0) { throw new Error('argument "path" is required !'); } //Obtain head object var head = document.getElementsByTagName('head')[0]; //Establish link Label and Insert into head Inside Label var link = document.createElement('link'); link.href = path; link.rel = 'stylesheet'; link.type = 'text/css'; head.appendChild(link); }
Time Conversion Function: Main function, talk about audio currentTime timestamp conversion to "minutes: seconds" display format.
//path Representation introduction CSS File Path //Time Display Conversion 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 }
Introduce json file function: file value json file path, callback only has to be a callback function, which is called when the file is loaded.
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: Since we are not talking about window incoming plug-ins, there are some methods that we cannot use, so we define the following method implementation to find the dom object in html through class.
//Determine if the plug-in exists " getElementsByClass",No, it will be implemented using the following method. if (!('getElementsByClass' in HTMLElement)) { //prototype As mentioned earlier, by " prototype"to HTMLElement Add Attribute Method 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; }
Parameter Merge Function: Object merge, which is mainly used for plug-in default parameter assignment. Use new parameters if set, or default parameters if not set
//Represents the original parameter, n Represents a new parameter, override Indicates whether to overwrite 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. Basic Functions
Parameter initialization with detailed comments.
_initial: function(opt) { // Default parameters var def = { skinID: "default", //Default skin path domID: "musicbox" //Set Player Container ID }; //If the function is initialized, merge when parameters are set, and use default parameters when no parameters are set this.def = extend(def, opt, true); //Be used for JSON File Storage Data this.data = {}; //Player initial volume is 0.3 this.sound = 0.3; this.currentID = 0; //Establish audion this.audion = document.createElement("AUDIO"); //Get Player dom object this.dom = document.getElementById(def.domID); //Player initial volume this.audion.volume = this.sound; //Define timer for progress bar adjustment, song scrolling, etc. this.timecolick; //Song Container this.songBox; //Player status, 0 for sequential play, 1 for circular play, and 2 for random play. this.isPlayState = 0; //Song lists are used to store song data this.songList; //Song playback progress bar this.songProgress; //Player head on playback progress bar this.songPlayHead; //Determine if the song is allowed to scroll, 0 means allowed, 1 means not allowed this.isSlide = 0; //Play progress, 0 indicates initial position this.playprogress = 0; //Maximum this.playMax = 0; //Whether the player is playing, 0 means playing, 1 means pausing this.isPlaying = 0; //Song List Scroll Distance this.scollHeight = 20; //Initialize the player and start playing this._GetData(); },
Player interface initializes and plays songs
//Set up player interface var _this = this;//Save the current object to_this //Initialization CSS file cssinto("skin/" + this.def.skinID + "/css/music.css"); //read json data readTextFile("skin/" + this.def.skinID + "/data.json", function(text) { //Data Read To data _this.data = JSON.parse(text); //Put interface HTML Code insertion container, interface initialization _this.dom.innerHTML = _this.data[0].MusicHtml; //Set Song List var htmlinsert = ""; //Past Song Container dom object _this.songBox = _this.dom.getElementsByClass(_this.data[0].musiclistbox)[0]; //Store song data _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; //Set Music List Click Events for (var i = 0; i < _this.songBox.childNodes.length; i++) { ( function(j) { _this.songBox.childNodes[j].onclick = function() { _this._PlaySong(j); } })(i) } //Once all the data is loaded, start playing the song _this._PlaySong(0);
Pause the playback function.
//Play stop button event _this.dom.getElementsByClass(_this.data[0].playBT)[0].onclick = function(e) { //Stop playing if it is playing if (_this.isPlaying == 0) { this.className = "playbutton"; _this.isPlaying = 1; _this.audion.pause() } else //Start playing if you stop playing { this.className = "pausebutton"; _this.isPlaying = 0; _this.audion.play(); } }
Song switch function, previous switch, next switch.
//Previous button _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) } //Next Button _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) }
Random play function, songs will be played randomly when the button is clicked.
//Random Play Button 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; }
Single loop function, the song will be played in a single loop when the button is clicked.
//Single Loop Button 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; }
Volume adjustment function, drag and drop volume adjustment function.
//Volume adjustment button 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'; //This sentence removes the selected effect window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty(); _this.audion.volume = to / max; //document.querySelector('.now') soundCurrentTime.style.width = to / max * 100 + '%'; } //Notice here that document To have a good dragging effect document.onmouseup = function() { document.onmousemove = null; }; }
Progress bar function.
//Get Progress Bar dom _this.songProgress = _this.dom.getElementsByClass(_this.data[0].SongProgress)[0]; //Get the playback head on the progress bar _this.songPlayHead = _this.dom.getElementsByClass(_this.data[0].playHead)[0]; //Click the progress bar to adjust the playback progress _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(); }; //Drag the player head to adjust the play progress _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)); //This sentence removes the selected effect window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty(); // _this.audion.currentTime = to / max; } //Notice here that document To have a good dragging effect document.onmouseup = function() { _this.isSlide = 0; _this.audion.currentTime = (_this.playprogress / _this.playMax) * _this.audion.duration; _this.audion.play(); document.onmousemove = null; };
Timing function function
//Timing function _this.timecolick = setInterval(function() { if (_this.isSlide == 1) { return; } //Set Progress Bar var percent = Math.floor(_this.audion.currentTime / _this.audion.duration * 10000) / 100 + "%"; _this.songPlayHead.style.left = percent; //Set the current play time _this.dom.getElementsByClass(_this.data[0].SingerCurrentTime)[0].innerHTML = conversion(_this.audion.currentTime); if (_this.audion.ended) { if (_this.isPlayState == 0) //Sequential Playback { if (_this.currentID < _this.songList.length - 1) { _this.currentID++; } else { _this.currentID = 0; } } else if (_this.isPlayState == 1) //Random Play { _this.currentID = Math.floor(Math.random() * _this.songList.length - 1) } else //Single tune circulation { _this.currentID = _this.currentID; } console.log(_this.currentID) _this._PlaySong(_this.currentID); } }, 100)
Song playback function
__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]) }
Song scrolling function.
_scollToMusiclist: function(singID, wmusicbox) { //ok 2019 On April 5, 2000, finally the debugging was successful. It is really not possible to keep the development going for a long time. Many things were unexpected. The reason why I just kept scrolling was that I didn't judge the maximum value. If it exceeds the maximum value, we need to assign the maximum value to the variable, so it won't keep blinking. 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, we have finished writing all our web players. I will package the source code and provide you with downloads and valuable comments.Single click download