音乐盒案例

Administrator
发布于 2020-07-24 / 10008 阅读 / 0 评论 / 1 点赞

音乐盒案例

音乐盒案例

  • 案例名称:音乐盒
  • 案例人员:杨标
  • 案例平台:HTML+CSS+JavasScript+jQuery+template+iconfont
  • 完成时间:2019年9月10日

::: tip 提示
本案例可下载
:::


效果图

PC端效果

image.png

手机端效果

image.png

案例说明

本题旨在对Ajax的知识进行的一个初始的认识,同时对jQuery进行一个补充与巩固,能够通过jQuery去进行日常的DOM操作要求,同时对模板引擎做一个回顾,也对音乐盒当中的歌词显示做一个学习

案例当中的数据来源于第三方平台

案例代码

本案例代码量大,可以在文档开头处进行下载

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>音乐播放器</title>
    <link rel="stylesheet" href="http://at.alicdn.com/t/font_1307637_jwii0cg7erj.css" type="text/css">
    <link rel="stylesheet" href="./css/msuic.css" type="text/css">
</head>

<body>
    <audio id="bgm" autoplay></audio>
    <div class="musicBox">
        <div class="m_ctl">
            <div class="left">
                <img src="./img/01.jpg" class="m_img">
                <span class="iconfont icon-bofang-yuanshijituantubiao"></span>
            </div>
            <ul class="right">
                <li>
                    <span class="m_name">Something Just Like This</span>
                    - <span class="m_author">The
                        Chainsmokers</span>
                </li>
                <li class="m_lrc"></li>
                <li>
                    <!--音乐进度条 -->
                    <div class="progress"></div>
                    <!--音乐的时间 -->
                    <div class="m_time">
                        <span>00:00</span>
                        /<span>04:07</span>
                    </div>
                    <!--右边的三个图标 -->
                    <div class="ctlIconBox">
                        <div class="volCtl">
                            <div class="volProgress"></div>
                             <span class="iconfont icon-yinliang-di"></span>
                        </div>
                        <span class="iconfont icon-youjiantou"></span>
                        <span class="iconfont icon-iconfront-"></span>
                    </div>
                </li>
            </ul>
        </div>
        <!--音乐列表 -->
        <div class="musicList">
            <!-- 在这个地方的事件绑定的时候,一定要使用委托 ,因为请求服务器是需要时间的
                在我们绑定事件的时候,有可能服务器的返回结果还没有回来
                这个时候,想要的就没有渲染进来 
                没有渲染渲染进来 ,页面上面就没有.m_item这个元素
                没有这些元素那么$(".musicList .m_item")就选不中了元素,
                选中不了元素就没有绑定事件

                在Ajax请求渲染的页面里面,它的事件一定是委托的
             -->
        </div>
    </div>
    <template id="m_item_temp">
        {{each musicList item index}}


        <div class="m_item">
            <div>{{index+1}}</div>
            <div>{{item.title}}</div>
            <div>{{item.author}}</div>
        </div>
        {{/each}}
    </template>
</body>
<script src="./js/jquery.js" type="text/javascript"></script>
<script src="./js/template-web.js" type="text/javascript"></script>
<script>
    $(function () {
        var musicList = [];
        var resultLrc = []; //存放当前音乐的歌词

        //用于存放我们请求回来的音乐列表数据
        //以后就做一件事情,如果缓存里面有数据,我们就不请求,如果没有数据 ,我们就请求
        if (localStorage.getItem("musicList")) {
            //说明有值
            musicList = JSON.parse(localStorage.getItem("musicList"));
            renderMusicList();
        } else {
            //没值
            var musicListUrl = "https://api.hibai.cn/api/index/index";
            $.post(musicListUrl, {
                TransCode: "020112",
                OpenId: "123456789",
                "Body[SongListId]": "141998290"
            }, function (data) {
                //数据在data.Body里面
                //我们要将这些数据转换成json,然后放到浏览器的缓存里面去
                localStorage.setItem("musicList", JSON.stringify(data.Body));
                musicList = data.Body;
                renderMusicList();
            });
        }

        //编写一个方法,渲染音乐列表
        function renderMusicList() {
            var html = template("m_item_temp", {
                musicList: musicList
            });
            $(".musicList").html(html);
        }

        //点击以后要让它添加一个背景颜色 
        $(".musicList").on("click", ".m_item", function () {
            //添加背景颜色 
            $(this).addClass("active").siblings().removeClass("active");

            var index = $(this).index();
            //获取当前点击的索引
            var obj = musicList[index];

            $(".m_img").attr("src", obj.pic);
            //图片赋值
            $(".m_name").text(obj.title);
            //歌名赋值
            $(".m_author").text(obj.author);
            //作者赋值
            $("#bgm").attr("src", obj.url);

            // Ajax 请求歌词  
            $.get(obj.lrc, function (lrc) {
                //lrc就是歌词  将歌词转换为数组
                resultLrc=parseLrcToArray(lrc);
            })

        });

        //对音乐 进行事件监听 
        $("#bgm").on("play", function () {
            //音乐播放
            $(".m_ctl .left .iconfont").removeClass("icon-bofang-yuanshijituantubiao").addClass(
                "icon-xinbaniconshangchuan-");
        }).on("pause", function () {
            //音乐暂停
            $(".m_ctl .left .iconfont").removeClass("icon-xinbaniconshangchuan-").addClass(
                "icon-bofang-yuanshijituantubiao");
        }).on("timeupdate", function () {
            //音乐播放的时候进行改变进度条
            var currentTime = this.currentTime;
            
            var duration = this.duration;
            $(".progress").css("background-size", currentTime / duration * 100 + "%");

            //设置时间
            $(".m_time>span").eq(0).text(toTime(currentTime));
            $(".m_time>span").eq(1).text(toTime(duration));

            //设置当前音乐的歌词
            if(resultLrc.length>0){
                //如果里面有数据,说明歌词取回来了
                for(var i in resultLrc){
                    if(currentTime<convertTimeToNum(resultLrc[i][0])){
                        var txt=resultLrc[i-1][1];
                        //设置在显示歌词的div里面
                        $(".m_lrc").html(txt);
                        break;
                    }
                }
            }
        }).on("canplay",function(){
            //绑定音乐的加载事件 
            //获取当前音量
            var vol = $("#bgm")[0].volume; 
            $(".volProgress").css("background-size","100% "+vol/1*100+"%");
        });
        //对音乐图标的点击事件
        $(".m_ctl .left .iconfont").click(function () {
            if ($("#bgm").attr("src")) {
                //有这个属性,我就操作,没这个属性,不管它
                //判断它是播放还是暂停   注意这是一个单属性
                if ($("#bgm").prop("paused")) {
                    //暂停的
                    $("#bgm")[0].play();
                    //注意这个地方要取索引0,取到直正的DOM对象,然后调用这个DOM的audio里面的play()方法去播放音乐 
                } else {
                    //说明音乐 是播放的
                    $("#bgm")[0].pause();
                }
            }
        });

        //与一个时间转换的方法  我给一个时间人你,你转换以后再给我
        function toTime(num) {
            var minutes = parseInt(parseInt(num) / 60).toString().padStart(2, "0");
            var second = parseInt(parseInt(num) % 60).toString().padStart(2, "0");
            return minutes + ":" + second;
        }

        //单击进度条以后的事件绑定
        $(".progress").click(function (event) {
            var length = event.clientX - this.getBoundingClientRect().left;
            //赋值进度条  [不需要设置,因为你设置时间以后,会触发一个事件 timeupdate]

            //还需要当前音乐音乐的时间
            $("#bgm")[0].currentTime = length / this.clientWidth * $("#bgm")[0].duration;
        })

        //将歌词转换为数组
        function parseLrcToArray(lrc) {
            var resultLrc = [];
            var arr = lrc.split("[");
            for (var i in arr) {
                var arr2 = arr[i].split("]");
                resultLrc.push(arr2);
            }
            return resultLrc;
        }
        //将歌词里面的时间转换为数字
        function convertTimeToNum(str) {
            var arr = str.split(":");
            return parseInt(arr[0]) * 60 + parseFloat(arr[1]);
        }
        //音量进度条的点击事件
        $(".volProgress").click(function(event){
            //当前鼠标的坐标-距离上面的坐标
            var height = event.clientY-this.getBoundingClientRect().top;
            //将音量的比例计算出来
            var volComputed=1- height/$(this).height();
            //对音量进行赋值
            $("#bgm")[0].volume = volComputed;
            $(".volProgress").css("background-size","100% "+volComputed*100+"%");
        });

        /*音乐列表上拉下划*/
        $(".icon-iconfront-").click(function(){
            $(".musicList").slideToggle(200);
        })
    });
</script>

</html>

在上面的JS代码里面,我们要注意一个事情就是在对DOM元素进行事件绑定的时候,如果这个元素是模板渲染出来的时候,我们要使用 jQuery里面的 on方法进行事件委托的绑定,不然事件是绑定不上去的

其次,歌词显示的时候,我们是对歌词做了个拆分与转换,转换成了一个二维数组,再去通过二维数组进行的对比歌词时间,parseLrcToArray()方法就是将歌词转换为二维数组的方法

再次convertTimeToNum(str)方法则是将转换成二维数组的歌词里面的时间进行转换,使其全部变成秒钟,这样就可以对比时间了


CSS代码

@charset "utf-8"; * {
    margin: 0px;
    padding: 0px;
    list-style-type: none;
}
.musicBox {
    width: 80%;
    margin: auto;  
    margin-top: 20px;
}
.m_ctl {
    height: 90px;
    display: flex;
    border: 1px solid lightgray;
}

.m_ctl .left {
    width: 90px;
    height: 90px;
    position: relative;
}

.m_img {
    width: 100%;
    height: 100%;
}

/*icon-xinbaniconshangchuan-*/
.m_ctl .left .iconfont {
    position: absolute;
    color: white;
    cursor: pointer;
}

.m_ctl .left .icon-bofang-yuanshijituantubiao {
    top: 50%;
    left: 50%;
    transform: translate(-50%,-50%);
    font-size: 32px;
}

/*暂停*/
.m_ctl .left .icon-xinbaniconshangchuan- {
    right: 5px;
    bottom: 5px;
    font-size: 26px;
}

.m_ctl .left .iconfont:hover {
    color: red;
    font-weight: bold;
}

.m_ctl .right {
    flex: 1;
    font-size: 12px;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    box-sizing: border-box;
    padding: 10px 5px;
}
/*歌曲的名子*/
.m_name {
    font-size: 14px;
}

/*歌词*/
.m_lrc {
    text-align: center;
}
/*音乐播放进度条*/
.progress {
    cursor: pointer;
    height: 3px;
    background-color: lightgray;
    flex: 1;
    margin-right: 20px;
    background-image: linear-gradient(to right,gray,gray);
    background-repeat: no-repeat;
    background-size: 0%;
}
.m_ctl .right li:last-child {
    display: flex;
    align-items: center;
}
/*音乐播放的时间*/
.m_time {
    margin-right: 20px;
}
.musicList {
    border: 1px solid #ececec;
    box-shadow: 0px 0px 15px 3px lightgray;
}
/*每一项音乐 */
.m_item {
    display: flex;
    font-size: 12px;
    padding: 10px 10px;
    border-top: 1px solid #ececec;
    cursor: pointer;
    color: #333;
}

/*点击以后的效果*/
.m_item.active {
    background-color: #ececec;
}

.m_item:hover {
    background-color: #ececec;
}

.m_item>div:nth-child(2) {
    flex: 1;
    margin-left: 10px;
}
.ctlIconBox{
    display:flex;
}
.volCtl{
    position:relative;
}
.volCtl:hover .volProgress{ 
    display:block;
}
.volProgress{
    width:16px;
    padding: 0 7px;
    width:3px;
    height:35px;
    background-color:lightgray;
    position:absolute;
    bottom:15px;
    left:50%;
    transform:translateX(-60%);
    background-image:linear-gradient(to right,gray,gray);
    background-repeat:no-repeat;
    background-size: 100% 0%;
    background-position:bottom;
    cursor:pointer;
    background-clip:content-box;
    display:none;
}
.icon-yinliang-di{
    cursor:pointer;
}
/*在手机端 访问的时候,出现全屏效果*/
@media only screen and (max-width:768px){
    .musicBox{
        width:100%;
        margin-top:0px;
    }
}

在CSS代码里面,有一点需要说明的就是音乐播放的进度条与音量的进度条都是通过背景实现的,我们通过了background-image:linear-gradient()的方式来实现

写在最后

本案例并非最终版本,还有一个效果就是音乐控制的时候,我们要实现随机播放顺序播放以及单取循环的功能 ,后期有时间会对此案例进行补充

同时本案例的API可进行抓包获取或查看后面的API列表


在线音乐盒API接口文档

文档说明

  • 运行平台:nodejs

  • 服务器语言:NodeJS+mysql

  • 服务器地址:api.hibai.cn

  • 编写人员:杨标

  • 编写时间:2019年9月10日星期二

一、获取音乐列表

  1. 描述

    该功能主要是用于获取当前的音乐列表

  2. 请求地址

    操作名称/api/index/index
    协议https
    请求方式POST
    URL全径请参考第5点
  3. 请求参数

    序号参数名中文名称必选数据类型长度备注
    1TransCode状态码YString 固定填写020112
    2OpenId接口编号YString 固定填写123456789
    3Body[SongListId]歌单列表YString 固定填写141998290
  4. 返回数据

    返回的数据是一个json数据对象,里面字段如下

    序号名称类型备注
    1BodyArray音乐列表
    2ErrCodeString错误编码,无错误为”OK”
    3ResultCodeString结果码,正常为1

    Body数组中对象描述如下

    序号名称类型备注
    1titleString歌曲名称
    2authorString歌曲演唱者
    3lrcString请求当前歌曲的歌词URL地址
    4picString当前歌曲的封面图片URL地址
    5urlString当前音乐的播放URL地址
  5. 请求URL实例

    https://api.hibai.cn/api/index/index

  6. 返回数据实例
    | 说明 | 实例 |
    | ------------ | ------------------------------------------------------------ |
    | Json数据实例 | {Body: [{title: "泡沫", author: "G.E.M.邓紫棋", pic: "https://api.hibai.cn/music/Music/Music?id=26113988&type=pic}ErrCode: "OK"ResultCode: 1} |


评论