HTML5移动端⾳乐播放器(启蒙篇)
这段时间公司⼀直在做⼀个PC的教育类单页应⽤,庞⼤复杂,涉及⾮常多H5的知识,⾳频就是其中的⼀部分。前些天偷台风的闲暇时写了⼀个移动端⾳乐播放器,作为练⼿项⽬(存放在Coding)。
在线地址:请点击
源码:请点击
注意:使⽤PC浏览最好打开移动设备模式,使⽤移动设备浏览需要关闭⽆痕浏览模式(否则⽆法使⽤本地存储,⼀般浏览器都是默认不开启),项⽬需要在本地服务器或线上服务器运⾏,以file:///形式的地址打开是⽆法进⾏ajax请求的,从⽽⽆法看到⾳乐数据。
项⽬实现的功能及所⽤知识
•
播放器的基础操作,上⼀⾸,下⼀⾸(顺序播放、随机播放、单曲循环),播放暂停,滑动时间轴的歌词定位世界最贵的车多少钱
•
初始handlebar模板渲染⾳乐列表数据,下拉滚动加载⾳乐列表数据。
•
歌曲列表可添加喜爱⾳乐,于下次刷新时更新喜爱⾳乐列表,基于HTML5本地存储。
•
布局采⽤rem布局,⾃适应移动端⼿机设备。
•
iconfont在线图标应⽤的使⽤
项⽬⽬录⽂件结构
项⽬⽬录⽂件结构
css:存放样式⽂件
lib:存放公共脚本库
js:存放项⽬脚本⽂件
img:存放图⽚
fonts:项⽬字体⽂件
res:项⽬⾳乐资源
ui:项⽬ui⽂件(psd)
// ============================配置变量================================var rootPath =
window.place(/\/\w+\.\w+/, "/");var Settings = {playmode: 0, //0列表循环,1随机,2为单曲循环volume:
0.5, //⾳量initNum: 10, //列表初始化歌曲数reqNum: 10 //后续请求歌曲数};// ============================⼯具
函数================================var Util = (function() {return {}})()//
============================Dom选择器================================var Dom = {}//
============================全局变量================================var winH =
$(window).height();var songNum = 0; //当前列表歌曲数⽬var lrcHighIndex = 0; // 歌词⾼亮索引var lrcMoveIndex = 0;
// 歌词移动单位索引var moveDis = 0; // 单句歌词每次移动距离var duration = 0; // 当前歌曲的时间var index = 0; //当前播放歌曲的索引var songInfo = null; // 当前歌曲信息var songModelUI = null; // 当前歌曲UI模型var timeArr = []; //当前歌曲时间数组var formatTimeArr = []; //当前歌曲时间数组(格式化为秒数)// ============================⼊⼝函数================================function main() {initUIFrame();var initModel = PlayerModel();var
songListUI = ModelUIFrame(Dom.songListContainer);var lsongListUI =
ModelUIFrame(Dom.lSongListContainer);SongList("data/data.json", function(data) {// ⽣成所有歌曲列表derList(data, 0, null, function() {songListUI.updateList();});// ⽣成喜爱歌曲列表
Util.addAnimationDelay(Dom.song);// 保存歌词数据initModel.saveLyric(data);});EventHandler();}//
============================初始化UI函数================================function initUIFrame() {}// ============================实现数据交互⽅法================================function
PlayerModel() {}// ============================模型动态UI模块
================================function ModelUIFrame(container) {}// ============================事件绑定模块================================function EventHandler() {}// 调⽤⼊⼝函数main();
功能点详解
Handlebar.js初次渲染及滚动加载
使⽤前端模板优点是把数据和结构分离出来,代码更清晰。但后来发现handlerbar.js似乎⽆法在js中⽰例模板对象,⽽html中的handlebar在初次进⼊页⾯便会被编译了,因此后续添加⾳乐还是采⽤传统的拼接字符串的⽅式,如果你有更优雅的动态加载⽅式,欢迎讨论交流。
html:handlebars模板包含在标签之中并且type类型为”text/x-handlebars-template”,在初始化页⾯的时候根据js获取数据植⼊后就渲染出相应的html。
< id="sListTpl" type="text/x-handlebars-template">{{#each this}}{{#isInitData this @index}}<li class="song btm-line"
data-src={{songSrc}} data-index={{id}}> <div class="poster"> <img src={{poster.thumbnail}}> </div> <div
class="songinfo"> <h2 class="lsongname">{{songName}}</h2> <sub class="lsinger">{{singer}}</sub> </div> <div class="loveflag"> <i class="icon icon-love {{#if loveFlag}}active{{/if}}"></i> </div></li>{{/isInitData}}{{/each}}</>
js:
function renderAllList(data) { var preTpl; var lsongArr = Item('lsonglist') === null ? [] :
JSON.Item('lsonglist')); // ⽣成列表 if (!sListTpl) { // 后续动态⽣成歌曲 var tpl = ""; var songIndex = songNum; $.each(data, function(index, el) { if (index >= songIndex && index < songIndex + qNum) { tpl += "<li class='song btm-line' data-src='res/music/" + songNum + ".mp3' data-index='" + songNum + "'><div
class='poster'>[站外图⽚上传中……(1)]</div><div class='songinfo'><h2 class='lsongname'>" + el.songName + "
class='poster'>[站外图⽚上传中……(1)]</div><div class='songinfo'><h2 class='lsongname'>" + el.songName + "
</h2><sub class='lsinger'>" + el.singer + "</sub></div><div class='loveflag'><i class='icon icon-love '></i></div> </li>"; songNum++; } }); $(container).append($(tpl)); } else { // ⾸次⽣成歌曲 preTpl = Handlebarspile(sListTpl);
$(container).html(preTpl(data)); } // 更新喜爱图标 if (lsongArr.length !== 0) { $.each(lsongArr, function(index, val) { Dom.songListContainer.find(".song").eq(val).find(".icon-love").addClass('active'); }); }}
rem布局⾃适应⽅案
⼤体上指的是html根元素上定义⼀个字体⼤⼩,然后css样式定义时使⽤rem作为单位,包括margin、paddding、⽤于绝对定位的单位等等。然后js根据⼿机设备的屏幕⼤⼩,改变根字体的⼤⼩,这样整个页⾯也会跟着相应的缩⼩或放⼤。
更多详解,请看这⼀篇⽂章《移动端⾃适应布局解决⽅案——rem》,您可以点击跳转。
关于歌词的同步⽅案实现
⽬前⾳乐播放器的歌词同步显⽰⼤概有两种,⼀种是精确到单个⽂字,⼀种是精确到单⾏歌词。本⽂实现的是第⼆种。
整体实现思路
银杏叶黄了美句页⾯初始化时,请求歌曲数据json(本地json⽂件模拟),其中歌名、歌⼿、图⽚等按需渲染到html中,将歌词存储到localStorage中。此时,F12打开chrome调试器,进⼊Application-LocalStorage可以看到:起公司名
点击⼀⾸歌进⼊播放页⾯后,歌词就会从本地存储中读取,此时你会看到⽣成这样的歌词结构:
每⼀⾏歌词都将要将歌词时间绑定在data-point上,监听歌曲播放的timeupdate事件,当歌曲的时间(经过取整处理)与当前data-point值相等时,就为当前歌词⾼亮(相当于给p添加current类名),并且根据当前⾼亮歌词的index索引将整个歌词盒⼦向上移动p标签的⾼度+margin-top的⾼度。
lrc歌词的结构
来⾃⽹易云⾳乐的歌词数据:
[00:14.64]如果不是那镜⼦\n[00:16.73]不像你不藏秘密\n[00:21.26]我还不肯相信\n[00:23.02]没有你我的笑更美丽\n[00:28.99]那天听你在电话⾥略带抱歉的关⼼\n[00:16.959]摘⼀颗苹果\n[00:19.800]等你从门前经过\n[00:22.700]送到你的⼿中帮你解渴\n[00:25.570]像夏天的可乐\n[00:00.00] 作曲 : 周杰伦\n[00:01.00] 作词 : 周杰伦
\n[00:05.620]\n[00:37.980]亲吻你的⼿\n
可以看到格式 = [时间点] + 要显⽰的⽂字 + n
延缓衰老这⾥有两个坑需要注意:
有的歌词秒数是精确到⼩数点后两位,有的是三位。
有的歌词(周杰伦《算什么男⼈》)格式是[时间点]+n
时间歌词创建映射
⾸先以n将歌词字符串分割成以[时间点]⽂字的数组,但由于这样分割之后最后⼀个元素是空的,所以⽤tempArr.splice(-1, 1)删除最后⼀个元素。
接下来循环遍历这个临时数组,由于上⾯提到的秒数精确度的问题,所以判断⼀下index为9是否为数字,若为数字则将该位数字删除。(采⽤字符串截取⽅式,若你对js字符串⽅法不熟悉,可以点击)
经过这样的处理之后,临时数组的元素格式不再有区别了,此时再进⾏字符串截取,将截取到的时间点放⼊timeArr,将截取的歌词放⼊lyricArr,并以返回保存着这两个变量的对象。
function createArrMap(lyric) { var timeArr = [], lyricArr = []; var tempArr = lyric.split("\n"); tempArr.splice(-1, 1); var tempStr = ""; $(tempArr).each(function(index) { tempStr = this; if (tempStr.charAt(9).match(/\d/) !== null) { tempStr = tempStr.substring(0, 9) + tempStr.substring(10); } timeArr.push(tempStr.substring(0, 10));
限额20万怎么解除lyricArr.push(tempStr.substring(10)); }); return { timeArr: timeArr, lyricArr: lyricArr };}
⽣成歌词
卡布西游怎么超进化由于上⾯歌词格式造成时间点对应的歌词为空,此时如果渲染出⼀个标签的⾼度将为0,这会影响歌词向上移动距离的不统⼀。因此下⾯作出个判断如果为空,则替换为“————–”。(为空的时候⼤多数是歌曲中间停顿或过渡的时候)
function renderLyric(songinfo) { var arrMap = ateArrMap(songinfo.lyric); var tpl = ""; $.each(ar
rMap.lyricArr, function(index, lyric) { var lyricContent = lyric === "" ? "--------------" : lyric; tpl += "<p class='' data-point='" +
arrMap.timeArr[index] + "'>" + lyricContent + "</p>"; }); Dom.lrcwrap.html(tpl);}
歌词同步
歌词同步我写在了syncLyric⽅法中,监听audio元素的timeupdate事件调⽤。
这个⽅法接收两个参数,第⼀个是当前播放歌曲时间(秒),第⼆个是转化为秒数的时间点数组。
这个⽅法接收两个参数,第⼀个是当前播放歌曲时间(秒),第⼆个是转化为秒数的时间点数组。
如果当前时间>=时间点,那么⾼亮当前歌词(以lrcHighIndex)存储,并且lrcHighIndex⾃增1。
当歌词⾼亮索引lrcHighIndex>=1即歌词⾼亮不为第⼀句时,计算索引并让歌词盒⼦向上移动。
function syncLyric(curS, formatTimeArr) { if (Math.floor(curS) >= formatTimeArr[lrcHighIndex]) {
Dom.lrc.eq(lrcHighIndex).addClass('current').siblings().removeClass('current'); if (lrcHighIndex >= 1) { lrcMoveIndex = lrcHighIndex - 2; moveDis += MoveDis(lrcMoveIndex); Dom.lrcwrap.animate
({ "top": "-" + moveDis + "px" }, 100); lrcMoveIndex++; } lrcHighIndex++; }}
原⽂链接
原作者
alex1504
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论