资讯专栏INFORMATION COLUMN

HTML5移动端音乐播放器(启蒙篇)

Lin_R / 2876人阅读

摘要:前些天偷台风的闲暇时写了一个移动端音乐播放器,作为练手项目。有的歌词周杰伦算什么男人格式是时间点时间歌词创建映射首先以将歌词字符串分割成以时间点文字的数组,但由于这样分割之后最后一个元素是空的,所以用删除最后一个元素。

这段时间公司一直在做一个PC的教育类单页应用,庞大复杂,涉及非常多H5的知识,音频就是其中的一部分。前些天偷台风的闲暇时写了一个移动端音乐播放器,作为练手项目。

在线地址:请猛击这里
源码:请猛击这里

注意:使用PC浏览最好打开移动设备模式,使用移动设备浏览需要关闭无痕浏览模式(否则无法使用本地存储,一般浏览器都是默认不开启),项目需要在本地服务器线上服务器运行,以file:///形式的地址打开是无法进行ajax请求的,从而无法看到音乐数据。

项目实现的功能及所用知识

播放器的基础操作,上一首,下一首(顺序播放、随机播放、单曲循环),播放暂停,滑动时间轴的歌词定位

初始handlebar模板渲染音乐列表数据,下拉滚动加载音乐列表数据。

歌曲列表可添加喜爱音乐,于下次刷新时更新喜爱音乐列表,基于HTML5本地存储。

布局采用rem布局,自适应移动端手机设备。

iconfont在线图标应用的使用

项目目录文件结构

css:存放样式文件
lib: 存放公共脚本库
js: 存放项目脚本文件
img: 存放图片
fonts: 项目字体文件
res: 项目音乐资源
ui:项目ui文件(psd)

// ============================配置变量================================
var rootPath = window.location.href.replace(//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);
initModel.getSongList("data/data.json", function(data) {
// 生成所有歌曲列表
songListUI.renderList(data, 0, null, function() {
songListUI.updateList();
});
// 生成喜爱歌曲列表
initModel.getLoveSongArr(function(lSongArr) {
lsongListUI.renderList(data, 1, lSongArr);
});
// 添加动画
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模板包含在script标签之中并且type类型为”text/x-handlebars-template”,在初始化页面的时候根据js获取数据植入后就渲染出相应的html。

js:

function renderAllList(data) {
    var preTpl;
    var lsongArr = Util.getItem("lsonglist") === null ? [] : JSON.parse(Util.getItem("lsonglist"));
    // 生成列表
    if (!sListTpl) {
        // 后续动态生成歌曲
        var tpl = "";
        var songIndex = songNum;
        $.each(data, function(index, el) {
            if (index >= songIndex && index < songIndex + Settings.reqNum) {
                tpl += "
  • [站外图片上传中……(1)]

    " + el.songName + "

    " + el.singer + "
  • "; songNum++; } }); $(container).append($(tpl)); } else { // 首次生成歌曲 preTpl = Handlebars.compile(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、paffffding、用于绝对定位的单位等等。然后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]如果不是那镜子
    [00:16.73]不像你不藏秘密
    [00:21.26]我还不肯相信
    [00:23.02]没有你我的笑更美丽
    [00:28.99]那天听你在电话里略带抱歉的关心
    
    
    [00:16.959]摘一颗苹果
    [00:19.800]等你从门前经过
    [00:22.700]送到你的手中帮你解渴
    [00:25.570]像夏天的可乐
    
    
    [00:00.00] 作曲 : 周杰伦
    [00:01.00] 作词 : 周杰伦
    [00:05.620]
    [00:37.980]亲吻你的手
    

    可以看到格式 = [时间点] + 要显示的文字 +
    这里有两个坑需要注意:
    有的歌词秒数是精确到小数点后两位有的是三位
    有的歌词(周杰伦《算什么男人》)格式是[时间点]+

    时间歌词创建映射

    首先以n将歌词字符串分割成以[时间点]文字的数组,但由于这样分割之后最后一个元素是空的,所以用tempArr.splice(-1, 1)删除最后一个元素。
    接下来循环遍历这个临时数组,由于上面提到的秒数精确度的问题,所以判断一下index为9是否为数字,若为数字则将该位数字删除。(采用字符串截取方式,若你对js字符串方法不熟悉,可以猛击这里)
    经过这样的处理之后,临时数组的元素格式不再有区别了,此时再进行字符串截取,将截取到的时间点放入timeArr,将截取的歌词放入lyricArr,并以返回保存着这两个变量的对象。

    function createArrMap(lyric) {
        var timeArr = [],
            lyricArr = [];
        var tempArr = lyric.split("
    ");
        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));
            lyricArr.push(tempStr.substring(10));
        });
        return {
            timeArr: timeArr,
            lyricArr: lyricArr
        };
    }
    生成歌词

    由于上面歌词格式造成时间点对应的歌词为空,此时如果渲染出一个
    标签的高度将为0,这会影响歌词向上移动距离的不统一。因此下面作出个判断如果为空,则替换为“————–”。(为空的时候大多数是歌曲中间停顿或过渡的时候)

    function renderLyric(songinfo) {
        var arrMap = Util.createArrMap(songinfo.lyric);
        var tpl = "";
        $.each(arrMap.lyricArr, function(index, lyric) {
            var lyricContent = lyric === "" ? "--------------" : lyric;
            tpl += "

    " + lyricContent + "

    "; }); 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 += Util.getMoveDis(lrcMoveIndex);
                Dom.lrcwrap.animate({
                    "top": "-" + moveDis + "px"
                }, 100);
                lrcMoveIndex++;
            }
            lrcHighIndex++;
        }
    }

    文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

    转载请注明本文地址:https://www.ucloud.cn/yun/80730.html

    相关文章

    • Vue 实现网易云音乐 WebApp

      摘要:基于等开发一款移动端音乐,界面参考了安卓版的网易云音乐布局适配常见移动端。图标使用阿里巴巴图标库,中间的唱片旋转动画使用了实现。搜索功能实现功能搜索歌手歌单歌曲热门搜索数据节流上拉刷新保存搜索记录。 基于 Vue(2.5) + vuex + vue-router + vue-axios +better-scroll + Scss + ES6 等开发一款移动端音乐 WebApp,UI ...

      Karuru 评论0 收藏0

    发表评论

    0条评论

    Lin_R

    |高级讲师

    TA的文章

    阅读更多
    最新活动
    阅读需要支付1元查看
    <