一、介绍
由于工作需求,原生的html5播放器虽然好用,但是ui不是太好看(个人觉得还可以)但是过不了设计的眼光,所以需要创建一个好看的播放器组件。一个组件的好处用面向对象的方式来讲就相当于一个类,你可以重复的去新建这个类,生成重复的组件对象。在这里,相当于你可以在同一个页面用new的方式,方便的创建多个自定义的播放器。
html5播放器原理:其实无非还是使用了html5的audio元素,然后基于audio的api自己去写播放器各个方法(播放、暂停、进度条、声音控制)等等,我看了下网易的云音乐的播放器,也是采用的audio的API来控制的。
原生audio控件:
网易云音乐皮肤:
笔者的audio皮肤(虽然挺一般的,但是跟公司的网站色系比较统一)
二、创建简单js组件原理
首先一个js插件关键的要点就是 不依赖、不污染、独立,不依赖与其它的方法和元素,独立自成一体。
最多基于jquery的插件会依赖于jquery,我也写过一些jquery的插件,但是这次要写的是完全基于原生js的插件。
使用IIFE架构
IIFE:immediately invoked functional expression 就是我们平常所说的立即执行函数:
(function() {
}());
然后我们需要一个构造函数来实现一个类,关于如何用js实现类可以参考一些js的书籍,这里我们使用构造函数的方式。在立即执行函数中的作用域是全局 window, 函数中的this 就代表的是 window ,如下先定义this.AudioPlayer 就等于window.AudioPlayer , 这样相当于AudioPlayer这个变量名称已经在全局作用域中使用了。
(function() {
this.AudioPlayer = function AudioPlayer() {
}
}());
然后建立一个对象就简单了,直接 var _audioPlayer = new AudioPlayer()就创建了一个对象。
传入参数
一般来讲,一个对象总会有一些初始化参数,比如我们的播放器AudioPlayer就可以有 音乐的url参数,是否需要音量控制的参数等等。 我们在构造函数的内部需要创建一个缺省的参数对象defaults,然后还需要有一个传入的参数和缺省的参数相互匹配的一个方法。
(function(){
this.AudioPlayer = function AudioPlayer() {
var config;
//定义缺省参数
var defaults = {
audiodom:'',
audiosrc:'',
showVolume:true
}
//传入的参数于缺省参数的对应
if (arguments[0] && typeof arguments[0] === "object") {
config = extendDefaults(defaults, arguments[0]);
}
}
function extendDefaults(source, properties) {
var property;
for(property in properties) {
if (properties.hasOwnProperty(property)) {
source[property] = properties[property];
}
}
return source;
}
})();
使用prototype创建公有方法
公有方法的作用很明显,可以创建这个对象的一些在外部需要调用的功能,如: 我在这里简单的写了一个当播放器切换音乐来源url的时候的一个公有方法,因为当需要换音乐url的时候,不需要再去重新创建一个播放器对象了。
AudioPlayer.prototype.setAudioSrc = function (src) {
this.audioPlayer.setAttribute('src',src);
}
整合在一起写就是这样:
(function(){
this.AudioPlayer = function AudioPlayer() {
var config;
//定义缺省参数
var defaults = {
audiodom:'',
audiosrc:'',
showVolume:true
}
//传入的参数于缺省参数的对应
if (arguments[0] && typeof arguments[0] === "object") {
config = extendDefaults(defaults, arguments[0]);
}
}
//公有方法
AudioPlayer.prototype.setAudioSrc = function (src) {
this.audioPlayer.setAttribute('src',src);
}
function extendDefaults(source, properties) {
var property;
for(property in properties) {
if (properties.hasOwnProperty(property)) {
source[property] = properties[property];
}
}
return source;
}
})();
总结:这样基本上写一个简单js组件的框架就搭建起来了,在里面不断的加入方法和功能就可以了。
三、编写一个html5播放器
以下就是一个播放器的js代码,css部分我就不贴出代码来了,附上github 的地址:
https://github.com/tangolivesky/audioplayer
(function () {
this.AudioPlayer = function AudioPlayer() {
var config;
// Define option defaults
var defaults = {
audiodom:'',
audiosrc:'',
showVolume:true
}
// Create options by extending defaults with the passed in arugments
if (arguments[0] && typeof arguments[0] === "object") {
config = extendDefaults(defaults, arguments[0]);
}
var duration;
var myAudioPlayer = document.createElement("div");
var audioPlayer = document.createElement("audio");
var playButton = document.createElement("button");
var timeLine = document.createElement("div");
var timeProgressBar = document.createElement("span");
var playhead = document.createElement("div");
var currentTime = document.createElement("div");
var volumeLine = document.createElement("div");
var volumeLineBar = document.createElement("span");
var volumeLineHead = document.createElement("div");
var volumeHorn = document.createElement('span');
var playheadSpan = document.createElement('span');
var timelineWidth = 230;
var volumelinewidth = 50;
var onplayhead = false;
var volumeStatus = [];
this.myAudioPlayer = myAudioPlayer;
this.playhead = playhead;
this.timeProgressBar = timeProgressBar;
this.timelineWidth = timelineWidth;
this.playButton = playButton;
this.currentTime = currentTime;
this.transTime = transTime;
this.intialStatus = intialStatus;
this.playStatus = playStatus;
this.volumeHorn = volumeHorn;
this.audioPlayer = audioPlayer;
myAudioPlayer.className = "audioplayer";
playButton.className = "playbutton play";
timeLine.className = "timeline";
timeProgressBar.className = "time-progress-bar";
playhead.className = "playhead intial";
playheadSpan.className = 'round';
currentTime.className = "current-time";
audioPlayer.setAttribute('controls', 'controls');
audioPlayer.style.display = "none";
volumeLine.className = "audio-line";
volumeLineHead.className = "audio-line-head";
volumeLineBar.className = "audio-line-bar";
volumeHorn.className = 'horn full';
if(config.audiodom === ''){
document.body.appendChild(myAudioPlayer);
}else{
document.getElementById(config.audiodom).appendChild(myAudioPlayer);
}
myAudioPlayer.appendChild(playButton);
playhead.appendChild(playheadSpan);
timeLine.appendChild(timeProgressBar);
timeLine.appendChild(playhead);
myAudioPlayer.appendChild(timeLine);
myAudioPlayer.appendChild(currentTime);
myAudioPlayer.appendChild(audioPlayer);
volumeLine.appendChild(volumeLineBar);
volumeLine.appendChild(volumeLineHead);
myAudioPlayer.appendChild(volumeHorn);
myAudioPlayer.appendChild(volumeLine);
if (config.hasOwnProperty("audiosrc")) {
audioPlayer.setAttribute('src', config.audiosrc);
}
else{
playButton.setAttribute("disabled", "disabled");
timeLine.setAttribute("disabled", "disabled");
volumeLine.setAttribute("disabled", "disabled");
playhead.setAttribute("disabled", "disabled");
volumeLineHead.setAttribute("disabled", "disabled");
volumeHorn.setAttribute("disabled", "disabled");
}
if(config.hasOwnProperty("showVolume")){
if(!config.showVolume){
volumeLine.style.display='block';
volumeLineHead.style.display='block';
volumeLineBar.style.display='block';
myAudioPlayer.style.width ='430px';
}
}
audioPlayer.addEventListener('loadedmetadata',function(){
currentTime.innerHTML = transTime(parseInt(audioPlayer.duration));
},false);
playButton.addEventListener('click', play, false);
audioPlayer.addEventListener('timeupdate', timeUpdate, false);
audioPlayer.addEventListener('canplaythrough', function () {
duration = audioPlayer.duration;
}, false);
timeLine.addEventListener('click', function (event) {
moveplayhead(event);
audioPlayer.currentTime = duration * clickPercent(event);
}, false);
playhead.addEventListener('mousedown', mouseDown, false);
window.addEventListener('mouseup', mouseUp, false);
volumeLine.addEventListener('click', function (event) {
movevolumehead(event);
audioPlayer.volume = volumeClickPercent(event);
if (audioPlayer.volume == 0) {
volumeHorn.className = 'horn';
}else if(audioPlayer.volume>=0.8){
volumeHorn.className = 'horn full';
}else if(audioPlayer.volume>=0.5){
volumeHorn.className = 'horn two';
}else if(audioPlayer.volume>0){
volumeHorn.className = 'horn one';
}
}, false);
//volumeLineHead.addEventListener('mousedown', volumeMouseDown, false);
//window.addEventListener('mouseup', volumeMouseUp, false);
//声音按钮控制
volumeHorn.addEventListener('click',function(event){
if (audioPlayer.volume > 0) {
volumeStatus[0] = audioPlayer.volume;
volumeStatus[1] = volumeHorn.className;
volumeStatus[2] = volumeLineBar.style.width;
volumeStatus[3] = volumeLineHead.style.marginLeft;
volumeHorn.className = 'horn';
movevolumehead(event);
audioPlayer.volume = volumeClickPercent(event);
}else{
audioPlayer.volume = volumeStatus[0];
volumeHorn.className = volumeStatus[1];
volumeLineBar.style.width = volumeStatus[2];
volumeLineHead.style.marginLeft = volumeStatus[3];
}
},false);
//样式调整
//设置成初始状态
function intialStatus(){
playhead.className = 'playhead intial';
}
//设置成播放状态
function playStatus(){
playhead.className = 'playhead';
}
function mouseDown() {
//样式调整
playStatus();
onplayhead = true;
window.addEventListener('mousemove', moveplayhead, true);
audioPlayer.removeEventListener('timeupdate', timeUpdate, false);
}
function mouseUp(e) {
//样式调整
if (parseInt(playhead.style.marginLeft) <= 0) {
intialStatus();
}
if (onplayhead == true) {
moveplayhead(e);
window.removeEventListener('mousemove', moveplayhead, true);
// change current time
audioPlayer.currentTime = duration * clickPercent(e);
audioPlayer.addEventListener('timeupdate', timeUpdate, false);
}
onplayhead = false;
}
function clickPercent(e) {
var timelineleft = getOffsetLeft(timeLine);
return (e.pageX - timelineleft) / timelineWidth;
}
function volumeClickPercent(e) {
var volume = 0;
var volumeLineLeft = getOffsetLeft(volumeLine);
if ((e.pageX - volumeLineLeft) / volumelinewidth < 0)volume = 0;
if ((e.pageX - volumeLineLeft) / volumelinewidth > 1)volume = 1;
if ((e.pageX - volumeLineLeft) / volumelinewidth >= 0 && (e.pageX - volumeLineLeft) / volumelinewidth <= 1)volume = (e.pageX - volumeLineLeft) / volumelinewidth;
return volume;
}
function moveplayhead(e) {
var timelineleft = getOffsetLeft(timeLine);
var newMargLeft = e.pageX - timelineleft;
if (newMargLeft >= 0 && newMargLeft <= timelineWidth) {
playhead.style.marginLeft = newMargLeft + "px";
timeProgressBar.style.width = newMargLeft * 100 / timelineWidth + "%";
}
if (newMargLeft < 0) {
playhead.style.marginLeft = "0px";
timeProgressBar.style.width = "0%";
}
if (newMargLeft > timelineWidth) {
playhead.style.marginLeft = timelineWidth + "px";
timeProgressBar.style.width = "100%";
}
}
function movevolumehead(e) {
var volumeLineLeft = getOffsetLeft(volumeLine);
var newMargLeft = e.pageX - volumeLineLeft;
if (newMargLeft >= 0 && newMargLeft <= volumelinewidth) {
volumeLineHead.style.marginLeft = newMargLeft-10 + "px";
volumeLineBar.style.width = newMargLeft * 100 / volumelinewidth + "%";
}
if (newMargLeft < 0) {
volumeLineHead.style.marginLeft = "-10px";
volumeLineBar.style.width = "0%";
}
if (newMargLeft > timelineWidth) {
volumeLineHead.style.marginLeft = volumelinewidth-10 + "px";
volumeLineBar.style.width = "100%";
}
}
function play() {
//样式控制
playStatus();
// start music
if (audioPlayer.paused) {
audioPlayer.play();
// remove play, add pause
playButton.className = "";
playButton.className = "playbutton pause";
} else { // pause music
audioPlayer.pause();
// remove pause, add play
playButton.className = "";
playButton.className = "playbutton play";
}
}
function timeUpdate() {
var playPercent = audioPlayer.currentTime / duration;
var playPercentWidth = timelineWidth * playPercent;
var playTime = transTime(parseInt(audioPlayer.currentTime));
currentTime.innerHTML = playTime;
playhead.style.marginLeft = playPercentWidth + "px";
timeProgressBar.style.width = playPercent * 100 + "%";
if (audioPlayer.currentTime == duration) {
playButton.className = "";
playButton.className = "playbutton play";
}
}
function transTime(time){
if (time<10) {
return('00:0'+time);
}else if(time<60){
return('00:'+time);
}else if(time<600){
var i = parseInt(time/60);
var j = parseInt(time%60);
if (j<10) {
return('0'+i+':0'+j);
}else{
return('0'+i+':'+j);
}
}else if(time<6000){
var i = parseInt(time/60);
var j = parseInt(time%60);
if (j<10) {
return(i+':0'+j);
}else{
return(i+':'+j);
}
}else{
return time;
}
}
function volumeMouseDown() {
onplayhead = true;
window.addEventListener('mousemove', movevolumehead, true);
//audioPlayer.removeEventListener('timeupdate', timeUpdate, false);
}
function volumeMouseUp(e) {
if (onplayhead == true) {
movevolumehead(e);
window.removeEventListener('mousemove', movevolumehead, true);
audioPlayer.volume = volumeClickPercent(e);
}
onplayhead = false;
}
function getOffsetLeft( elem )
{
var offsetLeft = 0;
do {
if ( !isNaN( elem.offsetLeft ) )
{
offsetLeft += elem.offsetLeft;
}
} while( elem = elem.offsetParent );
return offsetLeft;
}
}
AudioPlayer.prototype.getAudioPlayer = function () {
return this.myAudioPlayer;
};
AudioPlayer.prototype.setAudioSrc = function (src) {
this.audioPlayer.setAttribute('src',src);
}
// Utility method to extend defaults with user options
function extendDefaults(source, properties) {
var property;
for (property in properties) {
if (properties.hasOwnProperty(property)) {
source[property] = properties[property];
}
}
return source;
}
})();
在html中调用js播放器组件
在html调用就比较简单了,只需要一点点的代码,甚至不需要了解audio标签,就可以生成两个互不冲突的音乐播放器。
<div>
<div id="audio1">
</div>
<div id="audio2">
</div>
</div>
<script type="text/javascript" src="audioplayer.js"></script>
<script type="text/javascript">
var audioPlayer = new AudioPlayer({
audiodom:"audio1",
audiosrc:"http://www.alexkatz.me/codepen/music/interlude.mp3",
});
var audioPlayer2 = new AudioPlayer({
audiodom:"audio2",
audiosrc:"http://www.alexkatz.me/codepen/music/interlude.mp3",
});
</script>
结果如下:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。