“咳咳咳..在看正文之前,如果客官有兴趣可以轻点播放按钮聆听一下我的第一首吉他原创歌曲”。——作于大三下学期
“怎么感觉整个人都不好了…还有呜呜呜的声音”
“那时正值寒冬,于学校宿舍顶楼录制,周围一片漆黑,忽一阵冷风袭来,但对我而言那是美丽的协奏”
“什么鬼..!”
“好吧…那听听这首吧,也许能帮到客官您,这是我的第二首原创歌曲”。——作于大四上学期
“怎么比第一首调子还哀伤”?Are you kiding me?
“蓝瘦,香菇,在这里。”
好了,客观见笑了,为了真正治愈你,让我们回归正题吧!
这段时间公司一直在做一个PC的教育类单页应用,庞大复杂,涉及非常多H5的知识,音频就是其中的一部分。闲暇时写了一个移动音乐播放器,除了将之作为练手项目,对于音乐的喜爱也是促使自己想写个播放器的原因。
注意:使用PC浏览最好打开移动设备模式,使用移动设备浏览需要关闭无痕浏览模式(否则无法使用本地存储,一般浏览器都是默认不开启),项目需要在本地服务器或线上服务器运行,以file:///形式的地址打开是无法进行ajax请求的,从而无法看到音乐数据。
项目实现的功能及所用知识
- 播放器的基础操作,上一首,下一首(顺序播放、随机播放、单曲循环),播放暂停,滑动时间轴的歌词定位
- 初始handlebar模板渲染音乐列表数据,下拉滚动加载音乐列表数据。
- 歌曲列表可添加喜爱音乐,于下次刷新时更新喜爱音乐列表,基于HTML5本地存储。
- 布局采用rem布局,自适应移动端手机设备。
- iconfont在线图标应用的使用
项目目录文件结构
- css:存放样式文件
- lib: 存放公共脚本库
- js: 存放项目脚本文件
- img: 存放图片
- fonts: 项目字体文件
- res: 项目音乐资源
- ui:项目ui文件(psd)
项目js文件结构
|
|
功能点详解
Handlebar.js初次渲染及滚动加载
使用前端模板优点是把数据和结构分离出来,代码更清晰。但后来发现handlerbar.js似乎无法在js中示例模板对象,而html中的handlebar在初次进入页面便会被编译了,因此后续添加音乐还是采用传统的拼接字符串的方式,如果你有更优雅的动态加载方式,欢迎讨论交流。
html: handlebars模板包含在script标签之中并且type类型为”text/x-handlebars-template”,在初始化页面的时候根据js获取数据植入后就渲染出相应的html。
js:
|
|
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歌词的结构
来自网易云音乐的歌词数据:
可以看到格式 = [时间点] + 要显示的文字 + \n
这里有两个坑需要注意:
- 有的歌词秒数是精确到小数点后两位,有的是三位。
- 有的歌词(周杰伦《算什么男人》)格式是 [时间点]+\n
时间歌词创建映射
首先以\n将歌词字符串分割成以[时间点]文字的数组,但由于这样分割之后最后一个元素是空的,所以用tempArr.splice(-1, 1)删除最后一个元素。
接下来循环遍历这个临时数组,由于上面提到的秒数精确度的问题,所以判断一下index为9是否为数字,若为数字则将该位数字删除。(采用字符串截取方式,若你对js字符串方法不熟悉,可以猛击这里)
经过这样的处理之后,临时数组的元素格式不再有区别了,此时再进行字符串截取,将截取到的时间点放入timeArr,将截取的歌词放入lyricArr,并以返回保存着这两个变量的对象。
生成歌词
由于上面歌词格式造成时间点对应的歌词为空,此时如果渲染出一个
标签的高度将为0,这会影响歌词向上移动距离的不统一。因此下面作出个判断如果为空,则替换为“————–”。(为空的时候大多数是歌曲中间停顿或过渡的时候)
歌词同步
歌词同步我写在了syncLyric方法中,监听audio元素的timeupdate事件调用。
这个方法接收两个参数,第一个是当前播放歌曲时间(秒),第二个是转化为秒数的时间点数组。
如果当前时间>=时间点,那么高亮当前歌词(以lrcHighIndex)存储,并且lrcHighIndex自增1。
当歌词高亮索引lrcHighIndex>=1即歌词高亮不为第一句时,计算索引并让歌词盒子向上移动。