美文网首页
实现一个网易云音乐

实现一个网易云音乐

作者: 阿鲁提尔 | 来源:发表于2017-12-27 12:44 被阅读0次

    RT实现一个可以在线播放的移动端网易云音乐。

    tips:网易云音乐没用响应式,因为它做了两套网站。用户使用的PC端时,域名为http://music.163.com/;使用移动端时,域名为http://music.163.com/m/

    根据用户设备自动跳转相匹配页面,不是响应式
    PC端和手机端的区别
    • 屏幕不一样大
      PC端投放广告的数量,有很多选择主要用来放广告
      手机端界面小,不能乱放广告,放太多广告影响用户体验,点不到内容。
    • 触屏
      手机端可以触屏
      补充:现在PC端也有可以触屏的,如微软surface pro5
    • 没有IE
      手机端没有IE,可以兼容css3
    • 手机端省掉一些标签
      比如没有hover。

    界面

    开始

    • 创建文件夹
      mkdir music-demo
    
    • 变为git仓库
      git init
      //生成.git目录
    
    • 初始化一下仓库
      npm init  //回车到结束
      //生成package.json文件
    
    • 上传到本地库
      git add .
      git commit -v   ==> init
    

    编写网页

    • 创建页面
      touch home.html  playlist.html song.html
      // 创建首页  歌单页面  播放页面
    
    • 上传到本地仓库
      git add .
      git commit -v ==> add pages
      
      commit查看记录
      git log
      git show [commit字符串]  //查看修改代码 
    
    • 使用jQuery
      npm i jquery --save
      //安装到node_modules文件中
      使用ll node-modules/ 可以查看里面的文件
      包有jquery,就对了
    
    • 上传到本地库
      git add .
      git commit -v ==> install jquery
      git status
    
    • 测试jQuery能否能用
    vim home.html
    ==>
    <!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>
    </head>
    <body>
        <div id="hi"></div>
        <script src="./node_modules/jquery/dist/jquery.min.js"></script>
        <script>
            $('#hi').text('hello world')
        </script>
    </body>
    </html>
    ---
    :wq
    ---
    http-server -c-1 //模拟线上预览
    (如果没有http-server 请安装 npm install http-server -g)
    http://localhost:8080/home.html  //访问网页
    
      git add .
      git commit -v ==> init home.html
      git status
    

    补充:真正做开发时

    • 使用npm
    • 使用git
    • 使用http协议预览页面
    • 用gitignore 把node_modules 排除

    • 简化项目多余代码
      vi .gitignore  ==> /node_modules
      //把node_modules根目录隐藏,不上传到git仓库里
      //因为node_modules要安装很多需要的库,一个项目下来会特别大
    
    • 上传到本地库(别嫌麻烦,做好备份)
      git status 
      git add .gitignore
      git commit -v  ==> add ignore
      git status 
    
    • 上传到GitHub
      在GitHub新建空项目
      ...or push an existing repository from the command line
      代码抄写到git中
      //上传
    
    • 配置在线GitHub预览地址
      • Settings ↓↓↓
      • GitHub Pages ↓↓↓
      • Source ↓↓↓
      • master branch ---save
    • 预览
      预览页面会提示没有jQuery
      做到这一步的时候,因为我把node_modules上传到GitHub了。。
      删除了GitHub和本地的node_modules。
      所以需要重新安装一次jQuery
      npm i 
      ll node_modules //检查是否成功
      git add node_modules/jquery/dist/jquery.min.js
      会提示:
      // The following paths are ignored by one of your .gitignore files:
      // node_modules/jquery/dist/jquery.min.js
      // Use -f if you really want to add them.
      意思是:这个路径被你的gitignore给忽略了。如果你真的想添加他,使用 -f
      ok,everybody...
      git add node_modules/jquery/dist/jquery.min.js -f
      git commit -v   //add jquery
    
    • 编写首页
      html
    • 编写首页CSS
      使用动态REM
        <script>
            document.write(`
              <style>
                html{
                  font-size: ${document.documentElement.clienWidth/10}px;
                }
              </style>
            `)
        </script>
    

    我的博客:动态REM是什么,如何使用

    • 代码过程中遇到的问题

      • inline-block BUG: SVG在div中,底部有点空白,给svg加vertical-align: top; 可以解决空隙。
      • 边框宽度0.5px:width: 200%; height: 200%;transform: scale(.5);
      • 内容自动收缩

        方法一:li中加div,给div设置display:inline-block;vertical-align: top;
        方法二(推荐):给li设置display: flex; justify-content: center; align-items: center;
      • 文本多行省略
        display: -webkit-box;
        -webkit-box-orient: vertical;
        -webkit-line-clamp: 3;
        overflow: hidden;
        文章:CSS实现单行、多行文本溢出显示省略号
      • display:flex: 设置flex里面的div,不会出现边距合并。
      • calc()实现33.3333%布局
        calc-CSS| MDN
        ⅓ - 2px,三排减6px,把6px分在中间两个空隙。
      • GitHub上的node_modules中的JQ没有响应。
        cp node_modules/jquery/dist/jquery.min.js ./ //把jquery移动到当前目录下
        mkdir vendors //创建文件夹
        mv jquery.min.js vendors/ //把jQuery 移动到文件下
        git add . git status git commit -v ==> move jquery git push //上传
      • 背景图自动填满 background-size: cover;
        background-size- CSS | MDN 文章
      • 边距合并:
        方法一(不推荐):overflow:hidden。触发BFC,解决边距合并。
        方法二:使用伪类,::before ==> content:""; display:table。
        方法二升级版:添加公共:
        .no-collapse::before{ content: ""; display: table; }
        .no-collapse::after{ content: ""; display: table; }
    • 七牛云(存储数据,生成在线URL)
      把歌曲和图片等下载到本地,上传到七牛云生成在线URL。

      • 存储文件
        管理控制台 --> 对象存储 --> 内容管理 --> 上传文件 --> 复制外链
    • leanClound实现在线获取数据
      leanClound网站

      • 创建应用 ==> 创建class(设置限制写入) ==> 创建Playlist和Song
        写入数据时,可以界面写入,也可以使用代码。

      • 使用代码 ==> 查看文档。
        帮助 --> 文档 --> 数据存储开发指南·javascript --> SDK安装 -->
        $ npm install leancloud-storage --save
        安装在 node_modules/leancloud-storage/dist文件下 ll+该路径查看是否成功
        cp node_modules/leancloud-storage/dist/av-min.js vendors //拷贝
        ll vendors // 验证文件是否存在
        <script src="./vendors/av-min.js"></script> 添加到home.html中

      • 初始化
        var APP_ID = '6VgSKgCVd6rGXk9NI4hqQfO0-gzGzoHsz';
        var APP_KEY = 'Bs6J1KXLot6ifnFwYqePHdDo';
        AV.init({
        appId: APP_ID,
        appKey: APP_KEY
        });

      • 验证
        ping {{v2Domain}}
        然后在项目中编写如下测试代码:

        var TestObject = AV.Object.extend('TestObject');
        // 选择表名
        var testObject = new TestObject();
        // 生成一条数据
        testObject.save({
          words: 'Hello World!'
          //数据里面的内容
        }).then(function(object) {
          alert('LeanCloud Rocks!');
        })
        

        运行网页,如果成功,弹出"LeanCloud Rocks!"。


      • 例子:存一首歌

        var SongObject = AV.Object.extend('Song');
        var songObject = new SongObject();
        songObject.save({
          name: '像我这样的人',
          singer: '毛不易',
          url: 'http://oxklvemx3.bkt.clouddn.com/%E5%83%8F%E6%88%91%E8%BF%99%E6%A0%B7%E7%9A%84%E4%BA%BA.mp3'
        }).then(function(object) {
          alert('保存成功');
        })
        

        运行网页,如果成功,弹出"保存成功"。

      • 批量上传

        看文档 --> 数据存储开发指南·JavaScript --> 对象 --> 批量操作
        var SongObject = AV.Object.extend('Song');
        var songObject = new SongObject();
        songObject.set('name','1')
        songObject.set('singer','1')
        var songObject2 = new SongObject()
        songObject2.set('name','2')
        let songs = [songObject,songObject2]
        AV.Object.saveAll(songs)
        
      • 查询数据

        看文档 --> 数据存储开发指南·JavaScript --> 查询
        
        创建查询实例-->
        var query = new AV.Query('Todo');
        query.find().then(function (results) {
            console.log(results)
            //测试能否查询到
        }, function (error) {
        });
        在console.log输出所有歌曲信息,则查询成功-->
        
      • 使用数据

        最新音乐部分使用数据获取
        let $newList = $('ol#newList')
        // 创建查询
        var query = new AV.Query('Song');
        // query.startsWith('contain', 'true');
        query.find().then(function (results) {
            for( var i = 0;i<results.length;i++){
                let song = results[i].attributes
                let li = `
                <li>
                     <h3>${song.name}
                        <span>${song.reMark}</span>
                     </h3>
                     <p>
                        <svg class="icon icon-sq">
                          <use xlink:href="#icon-sq"></use>
                        </svg>
                        ${song.singer} - ${song.album}
                      </p>
                        <a class="play-button" href="#">
                            <svg class="icon icon-play">
                                <use xlink:href="#icon-play"></use>
                            </svg>
                        </a>
                    </li>
                `
                $newList.append(li)
            }
        }, function (error) {
        });
        
      • 添加加载动画
        仅CSS实现的加载动画 – Loader.css

        npm i --save loaders.css  //安装
        cp node_modules/loaders.css/loaders.min.css vendors  //拷贝
        vim home.html --> 添加引用
        <link rel="stylesheet" href="./vendors/loaders.min.css">
        选择一个喜欢的样式
        .ball-grid-pulse > div {   //假设加载动画为ball-grid-pulse
           background: orange;    // 添加颜色
        }
        $('#songs-loading').remove()  //获取到数据后移除
        
      • 条件查询

        文档 --> 数据存储开发指南·JavaScript --> 查询 --> SQL查询
        var cql = 'select * from Todo where [列名] = [查询值]';
        AV.Query.doCloudQuery(cql).then(function (data) {
            // results 即为查询结果,它是一个 AV.Object 数组
            var results = data.results;
        }, function (error) {
        });
        
    • 搜索页面

          文档 --> 数据存储开发指南·JavaScript --> 查询 --> 字符串查询
          $('input#search').on('input',function(e){
                let $input = $(e.currentTarget)  // 当前点击的input
                let value = $input.val()   //input的值
                var query = new AV.Query('Song');   //选择表名
                query.contains('name',value);    //查询包含输入值的歌曲名
                query.find().then(function(result){
                    console.log(result)    //输出符合条件的信息
                })
            })
            ---------------------------搜索歌曲(替换以上代码)
            var $searchResult = $('#searchResult')  //显示搜索结果的div
            $('input#search').on('input',function(e){
                let $input = $(e.currentTarget)
                let value = $input.val().trim()    //trim()去掉字符串前后的空格
                if(value ==""){return $searchResult.empty()}
                //空的清除显示结果
      
                使用or组合查询,搜索歌手和歌曲名包含字符串的数据
                var query1 = new AV.Query('Song');
                var query2 = new AV.Query('Song');
                query1.contains('name',value);
                query2.contains('singer',value);
                var queryAll = AV.Query.or(query1, query2);
      
                query.find().then(function(results){
                    $searchResult.empty()
                    if(results.length === 0){
                        $searchResult.html('没有结果')
                    }else{
                        for( var i = 0; i<results.length; i++){
                        let song = results[i].attributes
                        let li = `
                             <li data-id="${results[i].id}">
                                <a href="./song.html?id=results[i].id">
                                    ${song.name} - ${song.singer}</a>
                                    //注意a链接格式。
                                    //歌名和歌手在attributes中,id在results中
                            </li>
                        `
                        $searchResult.append(li)
                        }
                    }  
                })
            })
      
      • 搜索使用函数节流-解决请求过多
        异步的特点,发出去的请求,不一定按顺序相应。所以输入时给一个等待时间,等全部打完之后,再进行搜索。比如设置300毫秒,在300毫秒内输入,就不进行搜索,等到有300毫秒的停顿,就进行搜索。

        设置闹钟
         var timer = null;
         <--- 当在input输入的时候
         if(timer){   //如果有定时器,清除定时器
           window.clearTimeout(timer)  
         }   //没有定时器,设置一个定时器,300毫秒执行
         timer = setTimeout(function(){
           console.log('结束输入')  //测试使用,可注释
           // 在这输入以上搜索代码
           timer = null
         },300)   //一般数值在300~500毫秒
        
    • 播放页面

      • 转动的光盘

        使用关键帧
        @keyframes spin{
          0%{
              transform: rotate(0deg)
          }
          100%{
              transform: rotate(360deg)
          }
        }
        section.disk > .circle.playing{
            animation: spin 10s linear infinite;  // 线性 循环
        }
        section.disk > .circle.playing.pause{
            animation-play-state:paused;   暂停动画
        }
        

        W3C:动画 CSS3 animation 属性
        W3C:转动 CSS3 transform 属性
        CSS3 动画暂停 animation-play-state 属性

      • 垂直居中专辑

        //知道了宽高 160px
        position: absolute;
        top: 50%;
        left: 50%;
        margin-top: -80px;
        margin-left: -80px;
        
      • 虚化图片,并变暗

        filter: blur(100px) brightness(.2);
        
      • querystring 获取当前url的id值
        搜索 querystring
        JS获取URL中参数值(QueryString)的4种方法分享

        function GetQueryString(name) {  
            var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");  
            var r = window.location.search.substr(1).match(reg);  
            //获取url中"?"符后的字符串并正则匹配
            var context = "";  
            if (r != null)  
                context = r[2];  
            reg = null;  
            r = null;  
            return context == null || context == "" || context == "undefined" ? "" : context;  
        }
        var id = GetQueryString("id");
        
      • 每一次使用leancloud都需要初始化,所以把初始化新建个js,用的时候就引用

        mkdir scripts  
        touch av.js
        
        each ===>
        var APP_ID = '6VgSKgCVd6rGXk9NI4hqQfO0-gzGzoHsz';
        var APP_KEY = 'Bs6J1KXLot6ifnFwYqePHdDo';
        AV.init({
        appId: APP_ID,
        appKey: APP_KEY
        });
        
      • 通过id获取歌曲信息

        文档 --> 数据存储开发指南·JavaScript --> 对象 
        --> 获取对象 --> 获取objectId 
        var query = new AV.Query('Song');
        query.get(id).then(function (song) {
            console.log(song)
        }
        
      • 拷贝歌词,上传到leancloud

      • 通过正则获取当前歌词

        query.get(id).then(function (song) { // 选择表明,并搜索id
            let {url,lyric} = song.attributes      // 获取歌曲url和歌词
            // ES6语法,等同 let url = song.attributes.url
            let video = document.createElement('video')    //创建video
            video.src = url;        // 歌曲url
            video.play()          // 播放歌曲
            let array = []        
            var parts = lyric.split('\n')    //把歌词字符串分割成字符串数组
            parts.forEach(function(string,index){   //遍历数组
                let xxx = string.split(']')      //以]分割成字符串数组
                xxx[0] = xxx[0].substring(1)    //删除第一个字符"["
                // xxx[0] 是歌词 ; xxx[1] 是索引
                //把时间转换成秒
                let regex = /(\d+):([\d.]+)/   //正则获取时间
                let matches = xxx[0].match(regex)    
                let minute = +matches[1]
                let seconds = +matches[2]
        
                array.push({
                    time: minute*60+seconds,
                    lyric: xxx[1]
                })
            })
            // console.log(array)
            setInterval(function(){
                // console.log(video.currentTime)
                let current = video.currentTime
                for(let i = 0;i<array.length; i++){
                    if(i === array.length -1){  //没有最后一项
                        console.log(array[i].lyric)
                    }else if(array[i].time <= current && array[i+1].time > current){
                        console.log(array[i].lyric)
                        break;
                    }
                }
            },500)
        })
        
      • 把歌词添加到页面上

        <div class="lyric">
                <div class="lines"></div>
        </div>
        ----------------------------
        let $lyric = $('.lyric')
        array.map(function(object){
            let $p = $('<p/>')
            $p.attr('data-time',object.time).text(object.lyric)
             $p.appendTo($lyric.children('.lines'))
        })
        
      • 页面播放原理

        <audio src="//oxklvemx3.bkt.clouddn.com/xxx.mp3" controls></audio>
        // controls 会在浏览器显示控件
        
        ---  使用js向页面添加一个audio 并且把播放地址赋给url
         let audio = document.createElement('audio')
         audio.src = "//oxklvemx3.bkt.clouddn.com/xxx.mp3"
         audio.play()  //播放
        

        在线参考代码

      • 实现歌词滚动

        setInterval(function(){
        let seconds = video.currentTime
        // 获取音乐播放秒数
        let $lines = $('.lines > p')
        // 获取所有歌词标签
        let $whichLine
        for( let i = 0;i < $lines.length; i++){
            let currentLineTime = $lines.eq(i).attr('data-time')
            let nextLineTime = $lines.eq(i+1).attr('data-time')
            // let bug = $lines.eq(i+1).length !== 0   最后一行bug ==  $lines[i+1] !== undefined
            if( $lines[i+1] !== undefined && currentLineTime < seconds &&  nextLineTime > seconds ){
                $whichLine = $lines.eq(i)
                break;
            }
        }
        if($whichLine){
            $whichLine.addClass('active').prev().removeClass('active')
            let top = $whichLine.offset().top
            let linesTop = $('.lines').offset().top
            let delta = top - linesTop - $('.lyric').height()/3
            // 当前播放歌词距离顶部距离 -歌词容器距离顶部距离 - 一行歌词距离
            $('.lines').css(`transform`,`translateY(-${delta}px)`)
        }
        },500)

    相关文章

      网友评论

          本文标题:实现一个网易云音乐

          本文链接:https://www.haomeiwen.com/subject/bptrixtx.html