lkmusic 效果预览图(2016-4-26日已经改进)
更新:
优化界面效果
修复播放完成后不会自动切换处于暂停状态的bug
已经上传至github(敬请关注)
GitHub 项目仓库地址(欢迎访问):
https://github.com/laikedou/LMusic.git
<html lang="zh-cmn-Hans" >
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="renderer" content="webkit">
<meta name="version" content="1.0.0">
<link rel="stylesheet" href="http://data.smohan.net/static/css/demo.css">
<link rel="stylesheet" href="static/css/smusic.css"/>
<title>HTML5音乐播放器LKMusictitle>
head>
<body>
<div class="grid-music-container f-usn">
<div class="m-music-play-wrap">
<div class="u-cover">div>
<div class="m-now-info">
<h1 class="u-music-title"><strong>标题strong><small>歌手small>h1>
<div class="m-now-controls">
<div class="u-control u-process">
<span class="buffer-process">span>
<span class="current-process">span>
div>
<div class="u-control u-time">00:00/00:00div>
<div class="u-control u-volume">
<div class="volume-process" data-volume="0.50">
<span class="volume-current">span>
<span class="volume-bar">span>
<span class="volume-event">span>
div>
<a class="volume-control">a>
div>
div>
<div class="m-play-controls">
<a class="u-play-btn prev" title="上一曲">a>
<a class="u-play-btn ctrl-play play" title="暂停">a>
<a class="u-play-btn next" title="下一曲">a>
<a class="u-play-btn mode mode-list current" title="列表循环">a>
<a class="u-play-btn mode mode-random" title="随机播放">a>
<a class="u-play-btn mode mode-single" title="单曲循环">a>
div>
div>
div>
<div class="f-cb"> div>
<div class="m-music-list-wrap">div>
<div class="m-music-lyric-wrap">
<div class="inner">
<ul class="js-music-lyric-content">
<li class="eof">暂无歌词...li>
ul>
div>
div>
<div class="music-container-blur-bg">div>
div>
<script src="js/musicList.js">script>
<script src="js/wavesurfer.js">script>
<script src="js/util.js">script>
<script src="js/webaudio.js">script>
<script src="js/lkmusic_visualizer.js">script>
<script>
whenReady(function(){
new LMusic({
musicList : musicList,
autoPlay : true, //是否自动播放
defaultMode : 2, //默认播放模式,随机
offlineMode:true,
callback : function (obj) { //返回当前播放歌曲信息
}
});
});
script>
body>
html>
smusic.css
@charset "utf-8";
/**
* SMusic
* Author:Smohan
* Version:2.0.0
* url: http://www.smohan.net/lab/smusic.html
* 使用请保留以上信息
*/
/*reset*/
html,body,h1,h2,h3,h4,h5,h6,div,dl,dt,dd,ul,ol,li,p,blockquote,pre,hr,figure,table,caption,th,td,form,fieldset,legend,input,button,textarea,menu{margin:0;padding:0;-webkit-tap-highlight-color:rgba(0,0,0,0);word-wrap: break-word;font-size: inherit;line-height: inherit;overflow: visible;}
header,footer,section,article,aside,nav,address,figure,figcaption,menu,details{display:block;}
.f-cb{height: 0;}
.f-cb:after{display:block;content:" ";height:0;visibility:hidden;clear: both;}
.f-ib{display:inline-block;}
.f-din{display:inline;}
.f-dn{display:none;}
.f-db{display:block;}
.f-fl{float:left;}
.f-fr{float:right;}
.f-fwn{font-weight:normal;}
.f-fwb{font-weight:bold;}
.f-tal{text-align:left;}
.f-tac{text-align:center;}
.f-tar{text-align:right;}
.f-oh{overflow: hidden;zoom: 1;clear: both;}
.f-tdn{text-decoration: none!important;}
.f-vam,.f-vama *{vertical-align:middle;}
.f-wsn{word-wrap:normal;white-space:nowrap;}
.f-pre{overflow:hidden;text-align:left;white-space:pre-wrap;word-wrap:break-word;word-break:break-all;}
.f-wwb{white-space:normal;word-wrap:break-word;word-break:break-all;}
.f-ti{overflow:hidden;text-indent:-30000px;}
.f-lhn{line-height:normal;}
.f-toe{overflow:hidden;word-wrap:normal!important;white-space:nowrap;text-overflow:ellipsis;}
.f-usn{-webkit-user-select:none;user-select:none;}
.f-bsb{-webkit-box-sizing:border-box;box-sizing:border-box;}
.f-cp{cursor: pointer}
/*滚动条美化*/
::-webkit-scrollbar{width:6px;height:6px}
::-webkit-scrollbar-button:vertical{display:none}
::-webkit-scrollbar-track:vertical{background-color:transparent;}
::-webkit-scrollbar-track-piece{background-color:transparent;}
::-webkit-scrollbar-thumb:vertical{background-color:rgba(255,102,0,0.5);border-radius:6px}
::-webkit-scrollbar-thumb:vertical:hover,
::-webkit-scrollbar-thumb:vertical:active {background-color: #2e86ef}
html,body{
width: 100%;
height: 100%;
overflow: hidden;
font-family: "Microsoft Yahei","微软雅黑","Helvetica Neue","Hiragino Sans GB",Helvetica,Tahoma,sans-serif;
}
/*SMusic*/
.grid-music-container{
width: 625px;
height: 276px;
padding: 20px;
position: relative;
margin: 80px auto;
font-family: "Microsoft Yahei","微软雅黑","Helvetica Neue","Hiragino Sans GB",Helvetica,Tahoma,sans-serif;
}
.grid-music-container .music-container-spectrum{
width:100%;
height:100%;
position: absolute;
left:0;
top:0;
z-index: 2;
-webkit-filter: blur(3px);
}
.grid-music-container .music-container-blur-bg{
width:100%;
height:100%;
position: absolute;
left:0;
top:0;
z-index: 2;
background: url(../images/music_bg.jpg);
background-size: 100% 100%;
-webkit-filter: blur(5px);
}
.grid-music-container .m-music-play-wrap{
height: 150px;
position: relative;
padding-left: 140px;
z-index: 3;
}
.grid-music-container .u-cover{
width: 121px;
height: 121px;
overflow: hidden;
background: url(../images/music_icons_white.png) 0 0 no-repeat;
position: absolute;
top: 0;
left: 0;
}
.grid-music-container .u-cover img{
display: block;
width: 100%;
height: auto;
max-height: 100%;
border-radius: 50%;
}
.grid-music-container .u-cover.play{
-webkit-animation: Circle 10s linear infinite 0s forwards;
animation: Circle 10s linear infinite 0s forwards;
}
.grid-music-container .u-cover.paused{
-webkit-animation-play-state: paused;
animation-play-state: paused;
}
@-webkit-keyframes Circle {
from{
-webkit-transform: rotate(0deg);
}
to{
-webkit-transform: rotate(360deg);
}
}
@keyframes Circle {
from{
transform: rotate(0deg);
}
to{
transform: rotate(360deg);
}
}
.grid-music-container .m-now-info{
height: 100%;
}
.grid-music-container .m-now-info h1{
font-weight: normal;
}
.grid-music-container .m-now-info h1 strong{
font-size: 18px;
color: #fff;
font-weight: normal;
}
.grid-music-container .m-now-info h1 small{
margin-left: 20px;
font-size: 14px;
color: #fff;
}
.grid-music-container .m-now-controls{
padding-top: 30px;
position: relative;
}
.grid-music-container .m-now-controls .u-control{
display: inline-block;
vertical-align:middle;
font-size: 0;
overflow: hidden;
}
.grid-music-container .m-now-controls .u-process{
width: 220px;
height: 4px;
position: relative;
background-color: #cecfd4;
cursor: pointer;
}
.grid-music-container .m-now-controls .u-process .buffer-process,.grid-music-container .m-now-controls .u-process .current-process{
display: block;
width: 0;
height: 4px;
position: absolute;
top:0;
left: 0;
background-color: #FF6600;
z-index: 1;
}
.grid-music-container .m-now-controls .u-process .buffer-process{
z-index: 0;
background-color: #c1c2c0;
}
.grid-music-container .m-now-controls .u-time{
margin-left: 10px;
font-size: 12px;
color: #fff;
}
.grid-music-container .m-now-controls .u-volume{
overflow: visible;
text-align: center;
position: relative;
margin-left: 20px;
}
.grid-music-container .u-volume .volume-process{
width: 3px;
height: 50px;
background: #cecfd4;
position: absolute;
top: -54px;
left: 6px;
cursor: pointer;
visibility: hidden; /*设置不可见性,最好不要使用display:none,不然高度很难获取*/
}
.grid-music-container .u-volume .volume-process.show{
visibility: visible;
}
.grid-music-container .u-volume .volume-process .volume-current,
.grid-music-container .u-volume .volume-process .volume-event{
display: inline-block;
width: 3px;
height: 50%;
background-color: #FF6600;
position: absolute;
left: 0;
bottom:0;
-webkit-transition: height .2s linear;
transition: height .2s linear;
}
.grid-music-container .u-volume .volume-process .volume-event{
width: 21px;
left: -10px;
background: none;
height: 100%;
z-index: 1;
}
.grid-music-container .u-volume .volume-process .volume-bar{
display: inline-block;
width: 8px;
height: 8px;
border-radius: 100%;
background-color: #fff;
border: 1px solid #a8a9a7;
position: absolute;
left: -3px;
bottom: 50%;
-webkit-transition: bottom .2s linear;
transition: bottom .2s linear;
/* z-index: 2;*/
/* opacity: 0;*/
}
.grid-music-container .u-volume .volume-process .volume-bar:hover,
.grid-music-container .u-volume .volume-process .volume-bar:active{
background-color: #f1f2f0;
}
.grid-music-container .u-volume .volume-control{
display: inline-block;
width: 18px;
height: 18px;
background: url(../images/music_icons_white.png) -140px -80px no-repeat;
cursor: pointer;
}
.grid-music-container .u-volume .volume-control:hover{
background-position: -158px -80px;
}
.grid-music-container .u-volume .volume-control.muted{
background-position: -140px -98px;
}
.grid-music-container .u-volume .volume-control.muted:hover{
background-position: -158px -98px;
}
.grid-music-container .m-music-list-wrap{
margin-left: 135px;
margin-right: 25px;
height: 120px;
border: 1px solid rgba(255,255,255,.7);
overflow-x: hidden;
overflow-y: auto;
position: relative;
z-index: 3;
}
.grid-music-container .m-music-list-wrap ul{}
.grid-music-container .m-music-list-wrap li{
display: block;
line-height: 30px;
padding: 0 10px;
cursor: pointer;
color: #fff;
font-size: 14px;
}
.grid-music-container .m-music-list-wrap li strong{
font-size: 16px;
font-weight: normal;
}
.grid-music-container .m-music-list-wrap li:hover,.grid-music-container .m-music-list-wrap li.current{
background-color: rgba(255,255,255,.7);
color: #FF6600;
}
.grid-music-container .m-music-list-wrap li.current{
background-color: transparent;
}
.grid-music-container .m-play-controls{
margin-top: 10px;
}
.grid-music-container .m-play-controls a{
display: inline-block;
vertical-align: middle;
}
.grid-music-container .m-play-controls .u-play-btn{
display: inline-block;
width: 30px;
height: 30px;
margin-right: 15px;
cursor: pointer;
}
.u-play-btn{
background: url(../images/music_icons_white.png) -220px 0 no-repeat;
}
.u-play-btn.prev,.u-play-btn.next{
background-position: -220px 0;
}
.u-play-btn.prev:hover,.u-play-btn.next:hover{
background-position: -220px -36px;
}
.u-play-btn.prev{
-webkit-transform: rotate(-180deg);
transform: rotate(-180deg);
}
.u-play-btn.play{
background-position: -250px 0;
}
.u-play-btn.play:hover{
background-position: -250px -36px;
}
.u-play-btn.paused{
background-position: -280px 0;
}
.u-play-btn.paused:hover{
background-position: -280px -36px;
}
.u-play-btn.mode{
width: 20px!important;
height: 18px!important;
margin-right: 10px!important;
}
.u-play-btn.mode-list{
background-position: -181px -98px;
margin-left: 65px;
}
.u-play-btn.mode-list.current{
background-position: -221px -98px;
}
.u-play-btn.mode-random{
background-position: -201px -80px;
}
.u-play-btn.mode-random.current{
background-position: -241px -80px;
}
.u-play-btn.mode-single{
background-position: -181px -80px;
}
.u-play-btn.mode-single.current{
background-position: -221px -80px;
}
/*歌词*/
.grid-music-container .m-music-lyric-wrap{
width: 250px;
height: 100%;
position: absolute;
right: -250px;
top : 0;
border-left: 1px solid #fff;
overflow: hidden;
z-index: 1;
box-shadow: 0px 0px 10px rgba(0,0,0,0.5);
}
.grid-music-container .m-music-lyric-wrap .inner-bg{
width:100%;
height:100%;
background-color: rgba(0,0,0,.7);
-webkit-filter: blur(2px);
position: absolute;
left:0;
top:0;
z-index: 2;
}
.grid-music-container .m-music-lyric-wrap .inner{
position: absolute;
top:20px;
left: 20px;
right: 20px;
bottom: 20px;
overflow-x: hidden;
overflow-y: auto;
z-index: 3;
}
.grid-music-container .m-music-lyric-wrap .inner ul{
position: absolute;
width: 100%;
top:0;
left: 0;
overflow: hidden;
-webkit-transition: all .3s;
transition: all .3s;
}
.grid-music-container .m-music-lyric-wrap ul li{
display: block;
line-height: 30px;
height: 30px;
overflow: hidden;
text-align: center;
font-size: 13px;
color: #fff;
}
.grid-music-container .m-music-lyric-wrap ul li.current{
color:#FF6600;
font-size: 14px;
font-weight: bold;
}
.grid-music-container .m-music-lyric-wrap ul li.eof{
height: 100%;
line-height: 250px;
font-size: 20px;
color: #999;
}
musiclist.js
/**
* Created by Smohan on 2015/5/15.
*/
var musicList = [
{
title : '王铮亮-老朋友(电视剧《妈妈的花样年华》插曲)',
singer : '王铮亮',
cover : 'static/images/music_cover.jpg',
src : 'static/laopengyou.mp3',
lyric : "static/laopengyou.lrc"
},
{
title : '李克勤-月半小夜曲',
singer : '李克勤',
cover : 'static/images/music_cover.jpg',
src : 'static/yuebanxiaoyequ.mp3',
lyric : "static/yuebanxiaoyequ.lrc"
}
,
{
title : '彭佳慧-喜欢两个人',
singer : '彭佳慧',
cover : 'static/images/music_cover.jpg',
src : 'static/xihuanlianggeren.mp3',
lyric : "static/xihuanlianggeren.lrc"
}
];
wavesurfer.js
'use strict';
var WaveSurfer = {
};
var LkMusic = {
};
util.js
/* Common utilities */
WaveSurfer.util = {
extend: function (dest) {
var sources = Array.prototype.slice.call(arguments, 1);
sources.forEach(function (source) {
Object.keys(source).forEach(function (key) {
dest[key] = source[key];
});
});
return dest;
},
min: function(values) {
var min = +Infinity;
for (var i in values) {
if (values[i] < min) {
min = values[i];
}
}
return min;
},
max: function(values) {
var max = -Infinity;
for (var i in values) {
if (values[i] > max) {
max = values[i];
}
}
return max;
},
getId: function () {
return 'wavesurfer_' + Math.random().toString(32).substring(2);
},
ajax: function (options) {
var ajax = Object.create(WaveSurfer.Observer);
var xhr = new XMLHttpRequest();
var fired100 = false;
xhr.open(options.method || 'GET', options.url, true);
xhr.responseType = options.responseType || 'json';
xhr.addEventListener('progress', function (e) {
ajax.fireEvent('progress', e);
if (e.lengthComputable && e.loaded == e.total) {
fired100 = true;
}
});
xhr.addEventListener('load', function (e) {
if (!fired100) {
ajax.fireEvent('progress', e);
}
ajax.fireEvent('load', e);
if (200 == xhr.status || 206 == xhr.status) {
ajax.fireEvent('success', xhr.response, e);
} else {
ajax.fireEvent('error', e);
}
});
xhr.addEventListener('error', function (e) {
ajax.fireEvent('error', e);
});
xhr.send();
ajax.xhr = xhr;
return ajax;
}
};
/* Observer */
WaveSurfer.Observer = {
/**
* Attach a handler function for an event.
*/
on: function (event, fn) {
if (!this.handlers) { this.handlers = {}; }
var handlers = this.handlers[event];
if (!handlers) {
handlers = this.handlers[event] = [];
}
handlers.push(fn);
// Return an event descriptor
return {
name: event,
callback: fn,
un: this.un.bind(this, event, fn)
};
},
/**
* Remove an event handler.
*/
un: function (event, fn) {
if (!this.handlers) { return; }
var handlers = this.handlers[event];
if (handlers) {
if (fn) {
for (var i = handlers.length - 1; i >= 0; i--) {
if (handlers[i] == fn) {
handlers.splice(i, 1);
}
}
} else {
handlers.length = 0;
}
}
},
/**
* Remove all event handlers.
*/
unAll: function () {
this.handlers = null;
},
/**
* Attach a handler to an event. The handler is executed at most once per
* event type.
*/
once: function (event, handler) {
var my = this;
var fn = function () {
handler.apply(this, arguments);
setTimeout(function () {
my.un(event, fn);
}, 0);
};
return this.on(event, fn);
},
fireEvent: function (event) {
if (!this.handlers) { return; }
var handlers = this.handlers[event];
var args = Array.prototype.slice.call(arguments, 1);
handlers && handlers.forEach(function (fn) {
fn.apply(null, args);
});
}
};
/* Make the main WaveSurfer object an observer */
WaveSurfer.util.extend(WaveSurfer, WaveSurfer.Observer);
webaudio.js
'use strict';
LkMusic.WebAudio = {
scriptBufferSize: 256,
PLAYING_STATE: 0,
PAUSED_STATE: 1,
FINISHED_STATE: 2,
supportsWebAudio: function () {
return !!(window.AudioContext || window.webkitAudioContext);
},
getAudioContext: function () {
if (!LkMusic.WebAudio.audioContext) {
LkMusic.WebAudio.audioContext = new (
window.AudioContext || window.webkitAudioContext
);
}
return LkMusic.WebAudio.audioContext;
},
getOfflineAudioContext: function (sampleRate) {
if (!LkMusic.WebAudio.offlineAudioContext) {
LkMusic.WebAudio.offlineAudioContext = new (
window.OfflineAudioContext || window.webkitOfflineAudioContext
)(1, 2, sampleRate);
}
return LkMusic.WebAudio.offlineAudioContext;
},
init: function (params) {
this.params = params;
this.ac = params.audioContext || this.getAudioContext();
this.lastPlay = this.ac.currentTime;
this.startPosition = 0;
this.scheduledPause = null;
this.states = [
Object.create(LkMusic.WebAudio.state.playing),
Object.create(LkMusic.WebAudio.state.paused),
Object.create(LkMusic.WebAudio.state.finished)
];
this.createVolumeNode();
this.createScriptNode();
this.createAnalyserNode();
this.setState(this.PAUSED_STATE);
this.setPlaybackRate(this.params.audioRate);
},
disconnectFilters: function () {
if (this.filters) {
this.filters.forEach(function (filter) {
filter && filter.disconnect();
});
this.filters = null;
// Reconnect direct path
this.analyser.connect(this.splitter);
}
},
setState: function (state) {
if (this.state !== this.states[state]) {
this.state = this.states[state];
this.state.init.call(this);
}
},
// Unpacked filters
setFilter: function () {
this.setFilters([].slice.call(arguments));
},
/**
* @param {Array} filters Packed ilters array
*/
setFilters: function (filters) {
// Remove existing filters
this.disconnectFilters();
// Insert filters if filter array not empty
if (filters && filters.length) {
this.filters = filters;
// Disconnect direct path before inserting filters
this.analyser.disconnect();
// Connect each filter in turn
filters.reduce(function (prev, curr) {
prev.connect(curr);
return curr;
}, this.analyser).connect(this.splitter);
}
},
createScriptNode: function () {
if (this.ac.createScriptProcessor) {
this.scriptNode = this.ac.createScriptProcessor(this.scriptBufferSize);
} else {
this.scriptNode = this.ac.createJavaScriptNode(this.scriptBufferSize);
}
this.scriptNode.connect(this.ac.destination);
},
addOnAudioProcess: function () {
var my = this;
this.scriptNode.onaudioprocess = function () {
var time = my.getCurrentTime();
if (time >= my.getDuration()) {
my.setState(my.FINISHED_STATE);
my.fireEvent('pause');
} else if (time >= my.scheduledPause) {
my.setState(my.PAUSED_STATE);
my.fireEvent('pause');
} else if (my.state === my.states[my.PLAYING_STATE]) {
my.fireEvent('audioprocess', time);
}
};
},
removeOnAudioProcess: function () {
this.scriptNode.onaudioprocess = null;
},
createChannelNodes: function () {
var channels = this.buffer.numberOfChannels;
this.splitter = this.ac.createChannelSplitter(channels);
this.merger = this.ac.createChannelMerger(channels);
this.setChannel(this.params.channel);
this.analyser.disconnect();
this.analyser.connect(this.splitter);
this.merger.connect(this.gainNode);
},
setChannel: function (channel) {
var channels = this.buffer.numberOfChannels;
this.splitter.disconnect();
for (var c = 0; c < channels; c++) {
this.splitter.connect(this.merger, channel === -1 ? c : channel, c);
}
},
createAnalyserNode: function () {
this.analyser = this.ac.createAnalyser();
this.analyser.connect(this.gainNode);
},
/**
* Create the gain node needed to control the playback volume.
*/
createVolumeNode: function () {
// Create gain node using the AudioContext
if (this.ac.createGain) {
this.gainNode = this.ac.createGain();
} else {
this.gainNode = this.ac.createGainNode();
}
// Add the gain node to the graph
this.gainNode.connect(this.ac.destination);
},
/**
* Set the gain to a new value.
*
* @param {Number} newGain The new gain, a floating point value
* between 0 and 1. 0 being no gain and 1 being maximum gain.
*/
setVolume: function (newGain) {
this.gainNode.gain.value = newGain;
},
/**
* Get the current gain.
*
* @returns {Number} The current gain, a floating point value
* between 0 and 1. 0 being no gain and 1 being maximum gain.
*/
getVolume: function () {
return this.gainNode.gain.value;
},
decodeArrayBuffer: function (arraybuffer, callback, errback) {
if (!this.offlineAc) {
this.offlineAc = this.getOfflineAudioContext(this.ac ? this.ac.sampleRate : 44100);
}
this.offlineAc.decodeAudioData(arraybuffer, (function (data) {
callback(data);
}).bind(this), errback);
},
/**
* Compute the max and min value of the waveform when broken into
* subranges.
* @param {Number} How many subranges to break the waveform into.
* @returns {Array} Array of 2* peaks or array of arrays
* of peaks consisting of (max, min) values for each subrange.
*/
getPeaks: function (length) {
var sampleSize = this.buffer.length / length;
var sampleStep = ~~(sampleSize / 10) || 1;
var channels = this.buffer.numberOfChannels;
var splitPeaks = [];
var mergedPeaks = [];
for (var c = 0; c < channels; c++) {
var peaks = splitPeaks[c] = [];
var chan = this.buffer.getChannelData(c);
for (var i = 0; i < length; i++) {
var start = ~~(i * sampleSize);
var end = ~~(start + sampleSize);
var min = 0;
var max = 0;
for (var j = start; j < end; j += sampleStep) {
var value = chan[j];
if (value > max) {
max = value;
}
if (value < min) {
min = value;
}
}
peaks[2 * i] = max;
peaks[2 * i + 1] = min;
if (c == 0 || max > mergedPeaks[2 * i]) {
mergedPeaks[2 * i] = max;
}
if (c == 0 || min < mergedPeaks[2 * i + 1]) {
mergedPeaks[2 * i + 1] = min;
}
}
}
return (this.params.splitChannels || this.params.channel > -1) ? splitPeaks : mergedPeaks;
},
getPlayedPercents: function () {
return this.state.getPlayedPercents.call(this);
},
disconnectSource: function () {
if (this.source) {
this.source.disconnect();
}
},
destroy: function () {
if (!this.isPaused()) {
this.pause();
}
this.unAll();
this.buffer = null;
this.disconnectFilters();
this.disconnectSource();
this.gainNode.disconnect();
this.scriptNode.disconnect();
this.merger.disconnect();
this.splitter.disconnect();
this.analyser.disconnect();
},
load: function (buffer) {
this.startPosition = 0;
this.lastPlay = this.ac.currentTime;
this.buffer = buffer;
this.createSource();
this.createChannelNodes();
},
createSource: function () {
this.disconnectSource();
this.source = this.ac.createBufferSource();
//adjust for old browsers.
this.source.start = this.source.start || this.source.noteGrainOn;
this.source.stop = this.source.stop || this.source.noteOff;
this.source.playbackRate.value = this.playbackRate;
this.source.buffer = this.buffer;
this.source.connect(this.analyser);
},
isPaused: function () {
return this.state !== this.states[this.PLAYING_STATE];
},
getDuration: function () {
if (!this.buffer) {
return 0;
}
return this.buffer.duration;
},
seekTo: function (start, end) {
this.scheduledPause = null;
if (start == null) {
start = this.getCurrentTime();
if (start >= this.getDuration()) {
start = 0;
}
}
if (end == null) {
end = this.getDuration();
}
this.startPosition = start;
this.lastPlay = this.ac.currentTime;
if (this.state === this.states[this.FINISHED_STATE]) {
this.setState(this.PAUSED_STATE);
}
return { start: start, end: end };
},
getPlayedTime: function () {
return (this.ac.currentTime - this.lastPlay) * this.playbackRate;
},
/**
* Plays the loaded audio region.
*
* @param {Number} start Start offset in seconds,
* relative to the beginning of a clip.
* @param {Number} end When to stop
* relative to the beginning of a clip.
*/
play: function (start, end) {
// need to re-create source on each playback
this.createSource();
var adjustedTime = this.seekTo(start, end);
start = adjustedTime.start;
end = adjustedTime.end;
this.scheduledPause = end;
this.source.start(0, start, end - start);
this.setState(this.PLAYING_STATE);
this.fireEvent('play');
},
/**
* Pauses the loaded audio.
*/
pause: function () {
this.scheduledPause = null;
this.startPosition += this.getPlayedTime();
this.source && this.source.stop(0);
this.setState(this.PAUSED_STATE);
this.fireEvent('pause');
},
/**
* Returns the current time in seconds relative to the audioclip's duration.
*/
getCurrentTime: function () {
return this.state.getCurrentTime.call(this);
},
/**
* Set the audio source playback rate.
*/
setPlaybackRate: function (value) {
value = value || 1;
if (this.isPaused()) {
this.playbackRate = value;
} else {
this.pause();
this.playbackRate = value;
this.play();
}
}
};
LkMusic.WebAudio.state = {};
LkMusic.WebAudio.state.playing = {
init: function () {
this.addOnAudioProcess();
},
getPlayedPercents: function () {
var duration = this.getDuration();
return (this.getCurrentTime() / duration) || 0;
},
getCurrentTime: function () {
return this.startPosition + this.getPlayedTime();
}
};
LkMusic.WebAudio.state.paused = {
init: function () {
this.removeOnAudioProcess();
},
getPlayedPercents: function () {
var duration = this.getDuration();
return (this.getCurrentTime() / duration) || 0;
},
getCurrentTime: function () {
return this.startPosition;
}
};
LkMusic.WebAudio.state.finished = {
init: function () {
this.removeOnAudioProcess();
this.fireEvent('finish');
},
getPlayedPercents: function () {
return 1;
},
getCurrentTime: function () {
return this.getDuration();
}
};
WaveSurfer.util.extend(LkMusic.WebAudio, WaveSurfer.Observer);
lkmusic_visualizer.js
//注意此版本是支持音乐可视化的版本如果不需要音乐可视化的话那么可以使用lkmusic.js这个没有使用到AudioContext这个音频接口版本的js
//此版本完全使用audiocontext来进行的
//自用工具库
var whenReady = (function(){
var ready = false;
var funcs = [];//存储函数的数组
function handler(e){
if(ready) return;
if(e.type === 'readystatechange' && document.readyState !== 'commplete'){
return;
}
for(var i=0;i//进行标记
ready = true;
funcs = null;//置空
}
if(document.addEventListener){
document.addEventListener('DOMContentLoaded', handler,false);
document.addEventListener('readystatechange', handler,false);
window.addEventListener('load',handler,false);
}else{
//兼容IE等不支持addEventListener方法的浏览器
document.attachEvent('onreadystatechange',handler);
window.attachEvent('onload',handler);
}
return function isReady(f){
if(ready){
f.call(document);
}else{
funcs.push(f);
}
}
}());
//基于smusic开源代码进行修改和加强
(function(win,lkMusic,WaveSurfer){
//此音乐库采用全新的html5 api来对样式的增删改查进行操作 ie版本需要10.0以上 使用此音乐类库请特别注意低版本ie 已不再我的兼容列表里面
//至于为什么使用classList这个新的api 请参见张兴旭的一篇文章 http://www.zhangxinxu.com/wordpress/2013/07/domtokenlist-html5-dom-classlist-%E7%B1%BB%E5%90%8D/
//修正一下 实现了操作元素样式的操作类classList默认使用原生支持的api 否则使用模拟的api来进行实现
function classList(e){
if(e.classList){
//这里需要对classList的原生api进行一下加强
/*
DOMTokenList.prototype.adds = function(tokens){
tokens.split(' ').forEach(function(token){
this.add(token);
});
return this;
};*/
return e.classList;
}//如果原生支持classList这个api那么直接返回
else return new CSSClassList(e);//否则就伪造一个
}
//CSSClassList 是一个模拟DOMTokenList的javascript类
function CSSClassList(e){
this.e = e;
}
//如果e.className 包含类名c则返回true;否则返回false
CSSClassList.prototype.contains=function(c){
if(c.length ===0 || c.indexOf(' ')!==-1){
throw new Error("invalid class name: '"+c+"'");
}
//首先是常规检查
var classes = this.e.className;
if(!classes) return false;//e不含类名
if(classes === c) return true;//e有一个完全匹配的类名
//否则,把c自身看成一个单词,利用正则表达式搜索
//\b在正则表达式里面代表单词的边界
return classes.search('\\b'+c+'\\b') !== -1;
};
//如果c不存在,将c添加到e.className中
CSSClassList.prototype.add = function(c){
if(this.contains(c)) return;//如果存在就直接返回 不需要再新增一个
var classes = this.e.className;
if(classes && classes[classes.length-1] !== ' '){
c = ' '+c;//如果没有空格需要加上一个空格
}
this.e.className +=c;
};
//将在e.className中出现的c全部删除
CSSClassList.prototype.remove =function(c){
if(!this.contains(c))return;//如果类里面本来就没有就直接返回
//将所有作为单词的c和多余的尾随空格全部删除
var pattern = new RegExp('\\b'+c+'\\b\\s*','g');
this.e.className = this.e.className.replace(pattern,'');
};
//如果c不存在,将c添加到e.className中,并且返回true
//如果c存在,那么将在e.className中出现的所有c都删除,并返回false
CSSClassList.prototype.toggle=function(c){
if(this.contains(c)){//如果e.className 包含c
this.remove(c); //删除它
return false;
}else{
//否则的话
this.add(c);//否则就添加c
return true;
}
};
//返回 e.className本身
CSSClassList.prototype.toString = function(){
return this.e.className;
};
//返回e.className 中的类名 是一个数组
CSSClassList.prototype.toArray = function(){
return this.e.className.match(/\b\w+\b/g) || [];
}
//禁用右键菜单函数
function forbidenRightMenu(){
document.oncontextmenu=function(){
return false;
}
document.onmousedown=function(){
}
}
//实现链式调用
/**
* 实现基于jquery的链式调用
* @AuthorHTL
* @DateTime 2016-04-08T14:46:18+0800
* @param {[type]} query [description]
* @return {[type]} [description]
*/
function $(query){
return document.querySelector(query);
}
//实现基本拓展
/**
* 实现继承
* @AuthorHTL
* @DateTime 2016-04-08T14:46:43+0800
* @param {[type]} o [description]
* @param {[type]} p [description]
* @return {[type]} [description]
*/
function extend(o,p){
for(prop in p){
if(p.hasOwnProperty(prop)){
o[prop] = p[prop];
}
}
return o;
}
//实现计算时间
/**
* 对时间进行格式化
* @AuthorHTL
* @DateTime 2016-04-08T14:45:31+0800
* @param {[type]} time [description]
* @return {[type]} [description]
*/
function calctime(time){
var hours,minutes,seconds,timer='';
hours = String(parseInt(time / 3600));
minutes = String(parseInt((time % 3600)/60));
seconds = String(parseInt((time % 60)));
if(hours !== '0'){
if(hours.length === 1) hours ='0'+hours;
timer+=(hours+':');
}
if(minutes.length === 1 ){
minutes ='0'+minutes;
}
timer+=(minutes+":");
if(seconds.length === 1){
seconds = '0'+seconds;
}
timer+=seconds;
return timer;
}
//var cover =classList($('.u-cover')).remove('u-cover'); //测试代码
//实现兼容各个浏览器的XMLHttpRequest
if(win.XMLHttpRequest === undefined){
win.XMLHttpRequest = function(){
try{
return new ActiveXObject('Msxml2.XMLHTTP.6.0');
}catch(e1){
try{
return new ActiveXObject('Msxml2.XMLHTTP.3.0');
}catch(e2){
//否则就出错了
throw new Error('XMLHttpRequset is not supported!');
}
}
};
}
//实现简单的ajax操作
/**
* 简单的ajax封装
* @AuthorHTL
* @DateTime 2016-04-08T14:45:10+0800
* @param {[type]} url [description]
* @param {[type]} type [description]
* @param {[type]} isasync [description]
* @param {Function} callback [description]
* @return {[type]} [description]
*/
function ajax(url,type,isasync,callback){
if(!url) return false;
if(!type) type = 'GET';
if(!isasync) isasync = false;
if(!callback) callback = function(result){
console.log(result);
}
var xhr = new XMLHttpRequest();
xhr.open(type,url,isasync);//这里要注意是否异步,如果是true那就是异步调用不会卡死浏览器
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 && xhr.status=== 200){
var type = xhr.getResponseHeader('Content-Type');
if(type){
//检查类型
if(type.indexOf('xml') !== -1 && xhr.responseXML) callback(xhr.responseXML);//返回xml
else if(type === 'application/json')
callback(JSON.parse(xhr.responseText)); //如果是json那么就对json数据进行编码操作
else
//直接返回字符串响应
callback(xhr.responseText);
}else{
callback(xhr.responseText);
}
}
};
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");//让服务器判断是ajax请求还是属于普通请求
xhr.send(null);//发送请求
}
//ajax('http://localhost:808/smusicmyversion/index.html'); 测试代码
var bufferTimer = null;
/**
* 构造函数
* @AuthorHTL
* @DateTime 2016-04-08T14:44:48+0800
* @param {[type]} config [description]
*/
function LMusic(config){
//配置参数
this.config = extend(this.config,config);
this.musicList = this.config.musicList ||[];
this.musicLength = this.musicList.length;
if(!this.musicLength){
this.musicDom.listWrap.innerHTML = '暂无播放记录
';
}
this.audioDom = null;
this.audiocontext =null;//创建audiotext
this.audiobuffer = null;
this.gainnode = null;
this.buffersource = null;
this.curTime = 0;
this.Ajax = null;
this.tmpEvents = [];
this.init();//初始化播放器相关dom以及事件
}
LMusic.prototype ={
config:{
musicList:[],//播放列表
defaultVolume:0.5,//默认音量大小 0-1.0之间
defaultIndex:0,//播放列表索引
autoPlay:false,//是否默认自动播放
defaultMode:1,//播放模式 随机播放 按顺序播放 还是循环播放
bufferTime:1000,//缓冲的计时器时间
offlineMode:false, //是否支持离线缓存
showrightmenu:false,
gradientleft:'#0f0',
gradientmiddle:'#ff0',
gradientright:'#f00',
gradienttop:'#ff0',
callback:function(obj){
}
},
prepareAudioApi:function(){
//统一前缀
window.requestAnimationFrame = window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.msRequestAnimationFrame;
//处理当浏览器不支持AudioContext的情况,这样浏览器就不会报错了
if(LkMusic.WebAudio.supportsWebAudio()){
//如果支持webaudio那么就是下面代码
LkMusic.WebAudio.init(
{
audioContext:new (window.AudioContext || window.webkitAudioContext||window.mozAudioContext||window.msAudioContext)
}
);
}else{
console.log("对不起您的浏览器暂时不支持AudioContext!无法进行音乐可视化显示!");
}
},
/**
* 根据列表数组创建列表(以后可以使用List数据结构)
* @AuthorHTL
* @DateTime 2016-04-08T15:31:33+0800
* @return {[type]} [description]
*/
createListDom:function(){
var i =0,ul=''
;
for (var i = 0; i < this.musicLength; i++) {
ul += '' +this.musicList[i]['title']+' -- '+this.musicList[i]['singer']+'';
}
ul += '';
this.musicDom.listWrap.innerHTML = ul;
},
changeVolume:function(percent){
LkMusic.WebAudio.setVolume(percent);
},
/**
* 重置播放器
* @AuthorHTL
* @DateTime 2016-04-15T11:21:32+0800
* @param {[type]} idx [description]
* @return {[type]} [description]
*/
resetPlayer:function(idx){
(idx >= (this.musicLength-1)) && (idx = this.musicLength-1);
(idx <=0) && (idx =0);
var _this = this;
this.currentMusic = idx;
var nowMusic = this.musicList[idx];
this.loadBuffer(nowMusic.src);
clearInterval(bufferTimer);
this.musicDom.bufferProcess.style.width = 0+'px';
this.musicDom.curProcess.style.width = 0 + 'px';
this.musicDom.cover.innerHTML = ''" title="'+nowMusic.title + ' -- '+ nowMusic.singer + '">';
this.musicDom.title.innerHTML = ''+nowMusic.title+''+nowMusic.singer+'';
this.musicDom["lyricWrap"].innerHTML = '正在加载歌词... ';
this.musicDom["lyricWrap"].style.marginTop = 0 + "px";
this.musicDom["lyricWrap"].scrollTop = 0;
this.getLyric(idx);
//设置播放列表选中
var playlist = document.querySelectorAll('.m-music-list-wrap li'), i = 0;
for (var i = 0; i < this.musicLength; i++) {
if (i === idx) {
var cl = new classList(playlist[i]);
cl.add('current');
}else{
var cl = new classList(playlist[i]);
cl.remove('current');
}
}
var _callback = nowMusic;
_callback.index = idx;
typeof this.config.callback === 'function' && this.config.callback(_callback);
},
/**
* 控制音量播放
* @AuthorHTL
* @DateTime 2016-04-15T11:31:58+0800
* @param {[type]} volume [description]
*/
setVolume:function(volume){
_this = this;
var v = this.musicDom.volume,h=v.volumeEventer.offsetHeight||50;
(volume >=1) && (volume = 1);
(volume <=0) && (volume = 0);
var currentHeight = h*volume;//获取当前的音量高度
v.volumeCurrent.style.height = currentHeight +'px';
v.volumeCtrlBar.style.bottom = currentHeight +'px';
v.volumeProcess.setAttribute('data-volume', volume);
if(volume === 0){
var cl = new classList(v.volumeControl);
cl.add('muted');
LkMusic.WebAudio.setVolume(0);
}else{
var cl = new classList(v.volumeControl);
cl.remove('muted');
LkMusic.WebAudio.setVolume(volume);
}
},
clearTmpEvents: function () {
this.tmpEvents.forEach(function (e) { e.un(); });
},
cancelAjax: function () {
if (this.Ajax) {
this.Ajax.xhr.abort();
this.Ajax = null;
}
},
empty: function () {
if (!LkMusic.WebAudio.isPaused()) {
this.pause();
LkMusic.WebAudio.seekTo(0);
LkMusic.WebAudio.disconnectSource();
}
this.cancelAjax();
this.clearTmpEvents();
},
loadBuffer: function (url) {
this.empty();
// load via XHR and render all at once
return this.getArrayBuffer(url, this.loadArrayBuffer.bind(this));
},
/**
* Internal method.
*/
loadArrayBuffer: function (arraybuffer) {
LkMusic.WebAudio.decodeArrayBuffer(arraybuffer, function (data) {
this.loadDecodedBuffer(data);
}.bind(this));
},
/**
* Directly load an externally decoded AudioBuffer.
*/
loadDecodedBuffer: function (buffer) {
LkMusic.WebAudio.load(buffer);
this.fireEvent('ready',this);
},
getArrayBuffer: function (url, callback) {
var _this = this;
var ajax = WaveSurfer.util.ajax({
url: url,
responseType: 'arraybuffer'
});
this.Ajax = ajax;
this.tmpEvents.push(
ajax.on('progress', function (e) {
_this.onProgress(e);
}),
ajax.on('success', function (data, e) {
callback(data);
_this.Ajax = null;
}),
ajax.on('error', function (e) {
_this.fireEvent('error', 'XHR error: ' + e.target.statusText);
_this.Ajax = null;
})
);
return ajax;
},
onProgress: function (e) {
if (e.lengthComputable) {
var percentComplete = e.loaded / e.total;
} else {
// Approximate progress with an asymptotic
// function, and assume downloads in the 1-3 MB range.
percentComplete = e.loaded / (e.loaded + 1000000);
}
this.fireEvent('loading', Math.round(percentComplete * 100), this);
},
showProgress:function(percent,target){
target.setBuffer(percent,target.musicDom.bufferProcess);
},
drawSpectrum: function(analyser) {
var that = this,
canvas = document.querySelector('.music-container-spectrum'),
cwidth = canvas.width,
cheight = canvas.height - 2,
meterWidth = 10, //width of the meters in the spectrum
gap = 2, //gap between meters
capHeight = 2,
capStyle = this.config.gradienttop,
meterNum = 800 / (10 + 2), //count of the meters
capYPositionArray = []; store the vertical position of hte caps for the preivous frame
ctx = canvas.getContext('2d'),
gradient = ctx.createLinearGradient(0, 0, 0, 500);
gradient.addColorStop(1, this.config.gradientleft);
gradient.addColorStop(0.5, this.config.gradientmiddle);
gradient.addColorStop(0, this.config.gradientright);
var drawMeter = function() {
var array = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(array);
if (that.status === 0) {
//fix when some sounds end the value still not back to zero
for (var i = array.length - 1; i >= 0; i--) {
array[i] = 0;
};
allCapsReachBottom = true;
for (var i = capYPositionArray.length - 1; i >= 0; i--) {
allCapsReachBottom = allCapsReachBottom && (capYPositionArray[i] === 0);
};
if (allCapsReachBottom) {
cancelAnimationFrame(that.animationId); //since the sound is top and animation finished, stop the requestAnimation to prevent potential memory leak,THIS IS VERY IMPORTANT!
return;
};
};
var step = Math.round(array.length / meterNum); //sample limited data from the total array
ctx.clearRect(0, 0, cwidth, cheight);
for (var i = 0; i < meterNum; i++) {
var value = array[i * step];
if (capYPositionArray.length < Math.round(meterNum)) {
capYPositionArray.push(value);
};
ctx.fillStyle = capStyle;
//draw the cap, with transition effect
if (value < capYPositionArray[i]) {
ctx.fillRect(i * 12, cheight - (--capYPositionArray[i]), meterWidth, capHeight);
} else {
ctx.fillRect(i * 12, cheight - value, meterWidth, capHeight);
capYPositionArray[i] = value;
};
ctx.fillStyle = gradient; //set the filllStyle to gradient for a better look
ctx.fillRect(i * 12 /*meterWidth+gap*/ , cheight - value + capHeight, meterWidth, cheight); //the meter
}
that.animationId = requestAnimationFrame(drawMeter);
}
this.animationId = requestAnimationFrame(drawMeter);
},
hideProgress:function(target){
if(target.config.autoPlay){
target.play();
}else{
target.pause();
}
LkMusic.WebAudio.setVolume(target.config.defaultVolume);
target.action();
//开始进行音频频谱的绘制
target.drawSpectrum(LkMusic.WebAudio.analyser);
},
destroy:function(event){
},
/**
* 初始化播放
* @AuthorHTL
* @DateTime 2016-04-15T13:54:55+0800
* @return {[type]} [description]
*/
initPlay:function(){
var idx = this.config.defaultIndex;
if(this.config.defaultMode === 2){
//随机播放
idx = this.getRandomIndex();
}
var _this = this;
this.on('loading', this.showProgress);
this.on('ready', this.hideProgress);
this.on('destroy', this.destroy);
this.on('error', this.hideProgress);
LkMusic.WebAudio.on('finish', function () {
_this.playByMode('ended');
});
LkMusic.WebAudio.on('play', function () {
});
LkMusic.WebAudio.on('pause', function () {
});
LkMusic.WebAudio.on('audioprocess', function (time) {
});
this.resetPlayer(idx);
},
/**
* 播放音乐
* @AuthorHTL
* @DateTime 2016-04-15T14:09:29+0800
* @return {[type]} [description]
*/
play:function(start,end){
var ctrl = this.musicDom.button.ctrl;
LkMusic.WebAudio.play(start,end);
var ctrllist = new classList(ctrl);
ctrllist.remove('paused');
ctrllist.add('play');
ctrl.setAttribute('title', '暂停');
var coverlist = new classList(this.musicDom.cover);
coverlist.remove('paused');
coverlist.add('play');
},
/**
* 暂停音乐
* @AuthorHTL
* @DateTime 2016-04-15T14:09:38+0800
* @return {[type]} [description]
*/
pause:function(){
var ctrl = this.musicDom.button.ctrl;
LkMusic.WebAudio.pause();
var ctrllist = new classList(ctrl);
ctrllist.remove('play');
ctrllist.add('paused');
ctrl.setAttribute('title', '播放');
var coverlist = new classList(this.musicDom.cover);
coverlist.remove('play');
coverlist.add('paused');
},
/**
* 获取随机索引
* @AuthorHTL
* @DateTime 2016-04-15T13:59:03+0800
* @return {[type]} [description]
*/
getRandomIndex:function(){
var idx = this.currentMusic,len = this.musicLength,i=0,temp=[];
//将不是当前播放的推入到新的temp数组之中
for (var i = 0; i < len; i++) {
if(i !== idx){
temp.push(i);
}
}
var random = parseInt(Math.random() * temp.length);
return temp[random];
},
/**
* 根据播放模式来进行播放
* @AuthorHTL
* @DateTime 2016-04-15T14:41:01+0800
* @param {[type]} type [description]
* @return {[type]} [description]
*/
playByMode:function(type){
var mode = this.playMode,
idx = this.currentMusic,
len = this.musicLength,
index = idx;
switch (mode) {
case 1:
if(type === 'prev'){
index = ((idx<=len-1)&&(idx>0))?(idx-1):(len-1);
}else if(type === 'next' || type ==='ended'){
index = (idx >= len-1) ? 0 : (idx-1);//这种是三元运算符
}
break;
case 2:
index = this.getRandomIndex();//随机获取index
break;
case 3:
if(type === 'prev'){
index = ((idx<=len-1)&&(idx>0))?(idx-1):(len-1);
}else if(type === 'next'){
index = (idx >= len-1) ? 0 : (idx-1);//这种是三元运算符
}else{
index = idx;
}
break;
default:
//empty code here
break;
}
this.resetPlayer(index);
},
initRightPanel:function(options){
this.musicDom.music.addEventListener('',function(event){
event = event || window.event;
});
},
/**
* 一些操作
* @AuthorHTL
* @DateTime 2016-04-15T14:53:39+0800
* @return {[type]} [description]
*/
action:function(){
//这里为什么要用_this 这个要明白this在不同地方的指向不一样
var _this = this,v = this.musicDom.volume,btn=this.musicDom.button;
var enterFrame = function(){
var currenttime = calctime(LkMusic.WebAudio.getCurrentTime()),
totaltime = calctime(LkMusic.WebAudio.getDuration());
var currentProcess = (LkMusic.WebAudio.getCurrentTime()/LkMusic.WebAudio.getDuration()) *(_this.musicDom.bufferProcess.parentNode.offsetWidth);
_this.musicDom.time.innerHTML = ''+currenttime+'/'+totaltime;
_this.musicDom.curProcess.style.width = currentProcess +"px";
//歌词进度
var curTime = parseInt(LkMusic.WebAudio.getCurrentTime()*1e3);//这里不能跟着smusic那样写不然会出很大的错误
var lyrics = _this.musicDom['lyricWrap'].querySelectorAll('.u-lyric'),
sizes = lyrics.length,
i =0;
if(sizes >1){
for(;ivar lyl = lyrics[i];
var cl = new classList(lyl);
if(lyl){
var _time = parseFloat(lyl.getAttribute('data-time'));
if(curTime >= _time){
var top = (i-1) *30;//30是每个LI的高度
_this.musicDom['lyricWrap'].style.marginTop = -top +'px';
//移除之前的current
for(var j =0;jvar cl1 = new classList(lyrics[j]);
lyrics[j] && cl1.remove('current');
}
cl.add('current');
}
}
}
}
requestAnimationFrame(enterFrame);
};
requestAnimationFrame(enterFrame);
//音量控制器 静音和回复播放
v.volumeControl.addEventListener('click',function(event){
event = event || window.event;
event.stopPropagation();//阻止冒泡
var cl = new classList(v.volumeProcess);
if(cl.contains('show')){
cl.toggle('muted');
cl.contains('muted') ?(_this.changeVolume(0)) :(_this.changeVolume(_this.config.defaultVolume));
}else{
cl.add('show');
}
},false);
//当点击文档空白区域的时候隐藏音量调节器
document.addEventListener('click', function(event){
event = event || window.event;
event.stopPropagation();
var target = event.target || event.srcElement;
var cl = new classList(v.volumeProcess);
if((target.parentNode !== v.volumeProcess) && target.parentNode !==$('.grid-music-container .u-volume')){
cl.remove('show');
}
},false);
//绑定音量调节器
v.volumeEventer.addEventListener('click',function(event){
event = event || window.event;
event.stopPropagation();
var h = this.offsetHeight,
y = event.offsetY,
volume = (h-y)/h;
_this.setVolume(volume);
},false);
//绑定简单的播放列表以后我们使用数据结构的list来进行实现
var playlist = document.querySelectorAll('.m-music-list-wrap li'),i=0;
for(;i<_this.musicLength;i++){
!(function(i){
playlist[i].addEventListener('click',function(){
_this.resetPlayer(i);
},false);
}(i)
);
}
//绑定播放按钮
btn.ctrl.addEventListener('click',function(){
var cl = new classList(btn.ctrl);
if(cl.contains('play')){
_this.pause();
}else{
_this.play();
}
},false);
//绑定下一曲
btn.prev.addEventListener('click',function(){
_this.playByMode('prev');
},false);
//绑定下一曲
btn.next.addEventListener('click',function(){
_this.playByMode('next');
},false);
//这里我们还要根据外部配置或者默认配置的播放模式进行
switch (_this.playMode){
case 1:
var cl = new classList(btn.listCircular);
cl.add('current');
var cl1 = new classList(btn.singleCircular);
cl1.remove('current');
var cl2 = new classList(btn.randomPlay);
cl2.remove('current');
break;
case 2:
var cl = new classList(btn.randomPlay);
cl.add('current');
var cl1 = new classList(btn.listCircular);
cl1.remove('current');
var cl2 = new classList(btn.singleCircular);
cl2.remove('current');
break;
case 3:
var cl = new classList(btn.singleCircular);
cl.add('current');
var cl1 = new classList(btn.listCircular);
cl1.remove('current');
var cl2 = new classList(btn.randomPlay);
cl2.remove('current');
break;
default:
//empty code here
break;
}
//绑定列表循环
btn.listCircular.addEventListener('click',function(){
var cl = new classList(this);
cl.add('current');
var cl1 = new classList(btn.singleCircular);
cl1.remove('current');
var cl2 = new classList(btn.randomPlay);
cl2.remove('current');
_this.playMode = 1;
},false);
//绑定随机播放按钮
btn.randomPlay.addEventListener('click',function(){
var cl = new classList(this);
cl.add('current');
var cl1 = new classList(btn.listCircular);
cl1.remove('current');
var cl2 = new classList(btn.singleCircular);
cl2.remove('current');
_this.playMode = 3;
},false);
//绑定单曲循环
btn.singleCircular.addEventListener('click',function(){
var cl = new classList(this);
cl.add('current');
var cl1 = new classList(btn.listCircular);
cl1.remove('current');
var cl2 = new classList(btn.randomPlay);
cl2.remove('current');
_this.playMode = 3;
});
//拖动进度条
var $progress = this.musicDom['curProcess'].parentNode;
$progress.addEventListener('click',function(event){
event = event || window.event;
var left = this.getBoundingClientRect().left,width = this.offsetWidth;
var progressX = Math.min(width,Math.abs(event.clientX - left));//防止超出范围
//这里要特别注意不能讲currentTime 写成了currenttime
if(LkMusic.WebAudio.getCurrentTime() && LkMusic.WebAudio.getDuration()){
var start = parseInt((progressX/width) *(LkMusic.WebAudio.getDuration()));
LkMusic.WebAudio.play(start);
}
});
},
/**
* 缓冲加载
* @AuthorHTL
* @DateTime 2016-04-08T15:41:55+0800
* @param {[type]} audio [description]
* @param {[type]} bufferDom [description]
*/
setBuffer:function(percent,bufferDom){
var w = bufferDom.parentNode.offsetWidth;
var bufferWidth = percent/100*w;
bufferDom.style.width = bufferWidth+'px';
if(percent===100){
bufferDom.style.width = w +'px';
}
},
/**
* 获取歌词
* @AuthorHTL
* @DateTime 2016-04-14T16:48:12+0800
* @param {[type]} index [description]
* @return {[type]} [description]
*/
getLyric:function(index){
var _this = this;
if(this.lyricCache[index]){
this.renderLyric(this.lyricCache[index]);
}else{
var url = this.musicList[index]["lyric"];
if(url){
ajax(url,'GET',true,function(lrc){
var lyric = _this.parseLyric(lrc);
_this.lyricCache[index] = lyric ? lyric : null;
_this.renderLyric(lyric);
})
}else{
this.lyricCache[index] = null;
this.renderLyric(null);
}
}
},
/**
* 渲染歌词
* @AuthorHTL
* @DateTime 2016-04-14T16:48:23+0800
* @param {[type]} lyric [description]
* @return {[type]} [description]
*/
renderLyric:function(lyric){
var dom = this.musicDom["lyricWrap"], tpl = "";
if(lyric){
for(var k in lyric){
var txt = lyric[k] ? lyric[k] :'--- lkmusic ---';
tpl += ''">' +txt+'';
}
tpl && (tpl += 'www.laijiadayuan.com ');
}else{
tpl = '暂无歌词... ';
}
dom.style.marginTop = 0 + "px";
dom.screenTop = 0;
dom.innerHTML = tpl;
},
/**
* 解析歌词
* @AuthorHTL
* @DateTime 2016-04-14T15:29:02+0800
* @param {[type]} lrc [description]
* @return {[type]} [description]
*/
/*
[00:00.91]小苹果
[00:01.75]作词:王太利 作曲:王太利
[00:02.47]演唱:筷子兄弟
*/
parseLyric:function (lrc){
var lyrics = lrc.split("[");
var lrcObj = {};
for(var i=0;ivar lyric = decodeURIComponent(lyrics[i]);
var timeReg = /\d*:\d*((\.|\:)\d*)*\]/g;
var timeRegExpArr = lyric.match(timeReg);
if(!timeRegExpArr)continue;
var clause = lyric.replace(timeReg,'');
for(var k = 0,h = timeRegExpArr.length;k < h;k++) {
var t = timeRegExpArr[k];
var min = Number(String(t.match(/\d*/i)).slice(1)),
sec = Number(String(t.match(/\:\d*/i)).slice(1));
var time = (min * 60 + sec)*1e3;//必须转换成毫秒才行
lrcObj[time] = clause;
}
}
return lrcObj;
},
/**
* 初始化播放器
* @AuthorHTL
* @DateTime 2016-04-14T15:29:02+0800
* @return null [description]
*/
init:function(){
//缓存DOM结构
this.musicDom = {
music : $('.grid-music-container'),
cover : $('.grid-music-container .u-cover'),
title : $('.grid-music-container .u-music-title'),
curProcess : $('.grid-music-container .current-process'),
bufferProcess : $('.grid-music-container .buffer-process'),
time : $('.grid-music-container .u-time'),
listWrap : $('.grid-music-container .m-music-list-wrap'),
lyricWrap : $('.grid-music-container .js-music-lyric-content'), //歌词区域
volume : {
volumeProcess : $('.grid-music-container .volume-process'),
volumeCurrent : $('.grid-music-container .volume-current'),
volumeCtrlBar : $('.grid-music-container .volume-bar'),
volumeEventer : $('.grid-music-container .volume-event'), //主要作用于绑定事件,扩大了音量的触发范围
volumeControl : $('.grid-music-container .volume-control')
},
button : {
ctrl : $('.grid-music-container .ctrl-play'),
prev : $('.grid-music-container .prev'),
next : $('.grid-music-container .next'),
listCircular : $('.grid-music-container .mode-list'), //列表循环
randomPlay : $('.grid-music-container .mode-random'), //随机循环
singleCircular : $('.grid-music-container .mode-single') //单曲循环
}
};
this.currentMusic = this.config.defaultIndex ||0;
this.playMode = this.config.defaultMode||1;
this.lyricCache = {};
this.audioDom = document.createElement('audio');
this.createListDom();
//准备api
this.prepareAudioApi();
this.initPlay();
if(!this.config.showrightmenu){
forbidenRightMenu();
}
//支持离线缓存 (可以配置lkmusic.appcache)
if(this.config.offlineMode){
document.documentElement.setAttribute('manifest','lkmusic.appcache');
}//此功能暂时不起作用 因为必须把这个manifest先写在html中
//创建支持音乐频谱的cavas背景
var canvas = document.createElement('canvas');
canvas.className = 'music-container-spectrum';
this.musicDom.music.appendChild(canvas);
}
};
//这里是关键代码用来拓展观察者模式来着
Object.keys(WaveSurfer.Observer).forEach(function (key) {
LMusic.prototype[key] = WaveSurfer.Observer[key];
});
win = win || window;
win.LMusic = function(options){
return new LMusic(options);
};
})(window,LkMusic,WaveSurfer);