zoukankan      html  css  js  c++  java
  • 仿网易云音乐-微信小程序开发

    1.很多时候要找到完整的API接口很难,但网易云音乐的数据API是可以得到完整的。

    安装API:https://github.com/Binaryify/NeteaseCloudMusicApi,只需按照步骤部署就可以的。

    提示:由于本地电脑环境千差万别,建议会使用virtualBox虚拟机进行部署可一劳永逸

    部署完成,   cd NeteaseCloudMusicApi node app.js   开启服务即可

     2.根据相关API开发页面

    获取推荐歌单列表

     tabBar,主页面色,以及路由设计

    {
      "pages": [
        "pages/home/home",
        "pages/hot/hot",
        "pages/search/search",
        "pages/listdetail/listdetail",
        "pages/musicplay/musicplay"
    
      ],
      "window": {
        "backgroundColor": "#F6F6F6",
        "backgroundTextStyle": "light",
        "navigationBarBackgroundColor": "#d43c33",
        "navigationBarTitleText": "网易云音乐",
        "navigationBarTextStyle": "white"
      },
      "tabBar": {
        "backgroundColor": "#212121",
        "color": "#8F8F8F",
        "selectedColor": "#FFF",
        "list": [{
          "pagePath": "pages/home/home",
          "text": "推荐音乐",
          "iconPath": "/images/cm2_btm_icn_discovery.png",
          "selectedIconPath": "/images/cm2_btm_icn_discovery_prs.png"
          
        },
        {
          "pagePath": "pages/hot/hot",
          "text": "热歌榜",
          "iconPath": "/images/cm2_btm_icn_radio.png",
          "selectedIconPath": "/images/cm2_btm_icn_radio_prs.png"
        },
        {
          "pagePath": "pages/search/search",
          "text": "搜索",
          "iconPath": "/images/cm2_btm_icn_music.png",
          "selectedIconPath": "/images/cm2_btm_icn_music_prs.png"
        }
        ]
      },
      "requiredBackgroundModes": ["audio", "location"],
      "sitemapLocation": "sitemap.json"
    }
    tabBar,主页面色,以及路由设计
     "requiredBackgroundModes": ["audio", "location"],

    若需要在小程序切后台后继续播放音频,需要在 app.json 中配置 requiredBackgroundModes 属性。

    获取API数据,此时有两种方法,使用小程序云函数,或者本地获取

    本地获取:
    封装request请求get,post

    const baseurl = 'http://localhost:3000'
    function getbaseurl() {
      return baseurl;
    }
    //get请求
    function get(url, data) {
      return new Promise((reslove, reject) => {
        wx.request({
          method: 'GET',
          url: baseurl + url,
          data,
          success: reslove,
          fail: reject
        })
      })
    }
    
    //post请求
    function post(url, data) {
      return new Promise((reslove, reject) => {
        wx.request({
          method: 'POST',
          url: baseurl + url,
          data,
          success: reslove,
          fail: reject
        })
      })
    }
    
    //需要导出
    module.exports = {
      get,
      post,
      getbaseurl
    }
    request.js
    // pages/home/home.js
    var requestUrl = require('../../utils/request.js')
    Page({
    
      /**
       * 页面的初始数据
       */
      data: {
          musicList: [ ],
          detailUrl:''
      },
    
      /**
       * 生命周期函数--监听页面加载
       */
      onLoad: async function (options) {
        const res = await requestUrl.get('/personalized/?limit=6')
         this.setData({ musicList: res.data.result});
        console.log(res.data.result)
      },
    获取歌单列表

    自定义组件还可以自己触发双向绑定更新,做法就是:使用 setData 设置自身的属性

    <wxs module="common" src="../../utils/utils.wxs"></wxs>
    <view class="m-homeremd">
    <h2 class="remd_tl"> 推荐歌单</h2>
        <view class="remd_ul">
            <navigator url="../listdetail/listdetail?id={{item.id}}" class="remd_li"  data-musicid="{{item.id}}" wx:for="{{musicList}}" wx:key="index">
              <view class="remd_img">
                <image class="u-img" src="{{item.picUrl}}"></image>
                <span  class="u-earp remd_lnum">{{common.numberFormat(item.playCount)}}</span>
              </view>
              <text class="remd_text">{{item.name}}</text>
            </navigator>
        </view>
    </view>
    渲染歌单页面

    推荐歌单页面有一个播放量转换

    {{common.numberFormat(item.playCount)}}
    function numberFormat(value) {
        var param = {};
        var k = 10000,
          sizes = ['', '万', '亿', '万亿'],
          i;
        if (value < k) {
          param.value = value
          param.unit = ''
        } else {
          i = Math.floor(Math.log(value) / Math.log(k));
    
          param.value = ((value / Math.pow(k, i))).toFixed(2);
          param.unit = sizes[i];
        }
      return param.value + param.unit;
      }
    module.exports = {
      numberFormat: numberFormat
    }
    utils.wxs
    .m-homeremd {
        padding-top: 20px;
    }
    .m-homeremd .remd_tl {
        position: relative;
        padding-left: 9px;
        margin-bottom: 14px;
        font-size: 17px;
        height: 20px;
        line-height: 20px;
    }
    .m-homeremd .remd_tl:after {
        content: " ";
        position: absolute;
        left: 0;
        top: 50%;
        margin-top: -9px;
        width: 2px;
        height: 16px;
        background-color: #d33a31;
    }
    
    .m-homeremd .remd_ul{ 
      display: flex;
      flex-wrap: wrap;
    }
    .m-homeremd .remd_li{
      box-sizing: border-box;
      flex: 0 1 33.3%;
      padding-bottom: 20px;
    }
    .m-homeremd .remd_img>.u-img{ 
      width: 100px;
      height: 100px;
      }
      .m-homeremd .remd_text {
        display: -webkit-box;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
        overflow: hidden;
        padding: 6px 2px 0 6px;
        min-height: 30px;
        line-height: 1.2;
        font-size: 13px;
    }
    .m-homeremd .remd_img {
        position: relative;
    }
    .m-homeremd .remd_lnum {
        position: absolute;
        right: 5px;
        top: 2px;
        z-index: 3;
        padding-left: 13px;
        color: #fff;
        font-size: 12px;
        background-position: 0;
        background-repeat: no-repeat;
        background-size: 11px 10px;
        text-shadow: 1px 0 0 rgba(0,0,0,.15);
    }
    .u-earp {
        background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAABfUlEQVR4AZXSA0y/YRAH8PvbdhqyrbnmMDPXFIYwZmt2bo5jDRlDtu1mZHc9z737/Z7t4n833ffu8+p5Af+zePAZQ7AaB3AF57Ebq0X35X7wBhNxEXdZLWKSmNwB/mATW1XVjH84+IdD2nC/tyclLuC9u5l3QmBXyn6vlorpPwXkw7TK+HqjJ+WVG7iqeuXWlXK9QaQV3ygQQ+ubFVFiyRp+gRjBc3gLP0XnWh11vUkkRgc+45wMetPBBX7xzyiQS28agTmxSSBYtof9X9zg051f/vMXt8N+IqEErqpl05/Dr64KfvVnyx2xKcHFsGyKA+DZveBZob/cuRgigNui2YE/D/4Qf2hrm4D2peHdg+CdtoUKoC+OifLhq/pcgWt6JBylYIADfa4eabH0amW5jGJRHOjypfKrlaUyAuAif4LHgParaMAYnMHkMUBbxgh8wAHPFTin/+l0ggOWK9AYdzZzPl8bwwDPFQBHei17DlTOwVdwEMMvHPAc8D/rBs0Gmx5Y/w/0AAAAAElFTkSuQmCC');
    }
    css样式

    列表页面渲染:根据id值跳转到相对应的列表页面

    <navigator url="../listdetail/listdetail?id={{item.id}}" class="remd_li"  data-musicid="{{item.id}}" wx:for="{{musicList}}" wx:key="index">
    import {get} from "../../utils/request.js"
    // pages/listdetail/listdetail.js
    Page({
    
      /**
       * 页面的初始数据
       */
      data: {
        detailList:[ ]
      },
    
      /**
       * 生命周期函数--监听页面加载
       */
      onLoad: async function (options) {
        console.log(options)
        const url =`/playlist/detail?id=${options.id}`
        const res = await get(url)
        console.log(res.data)
        this.setData({ detailList: res.data.playlist})
        
      },
    listdetail.js
    <!-- <wxs module="common" src="../../utils/utils.wxs"></wxs>
    <view class="u-plhead pylst_header">
        <view class="plhead_bg" style="background-image:url({{detailList.playlist.coverImgUrl}})">
        </view>
        <view class="plhead_wrap">
            <view class="plhead_fl lsthd_fl">
          <image src="{{detailList.playlist.coverImgUrl}}" class='u-img'>{{detailList.playlist.coverImgUrl}}</image>
                <span class="lsthd_icon">歌单</span>
                <i class="u-earp lsthd_num">{{common.numberFormat(detailList.playlist.playCount)}}</i>
            </view>
            <view class="plhead_fr">
                <h2 class="f-thide2 f-brk lsthd_title">{{detailList.playlist.name}}</h2>
                <view class="lsthd_auth f-thide">
                    <a class="lsthd_link" href="">
                        <view class="u-avatar lsthd_ava">
                        <image class="u-img" src="{{detailList.playlist.creator.avatarUrl}}"></image>
                        <span class="ava-icon ava-icon-daren"></span>
            
                        </view>
                {{detailList.playlist.creator.nickname}}
                    </a>
                </view>
            </view>
        </view>
    </view>
    <h3 class="u-smtitle">歌曲列表</h3>
    <view class="m-sglst"wx:for="{{detailList}}" wx:key="index">
    <view class="m-list"wx:for="{{item.tracks}}" wx:for-item="item" wx:key="index">
    <navigator class="m-sgitem"url="/">
            <view class="sgfl">{{index+1}}</view>
            <view class="sgfr f-bd f-bd-btm">
                <view class="sgchfl">
                    <view class="f-thide sgtl">{{item.name}}</view>
                    <view class="f-thide sginfo">{{}}</view>
                </view>
                <view class="sgchfr">
                    <span class="u-hmsprt sgchply">
                    </span>
                </view>
            </view>
    </navigator> 
     </view>
    </view> -->
    <wxs module="common" src="../../utils/utils.wxs"></wxs>
    <view class="u-plhead pylst_header">
        <view class="plhead_bg" style="background-image:url({{detailList.coverImgUrl}})">
        </view>
        <view class="plhead_wrap">
            <view class="plhead_fl lsthd_fl">
          <image w-if="detailList.coverImgUrl" src="{{detailList.coverImgUrl}}" class='u-img'></image>
                <span class="lsthd_icon">歌单</span>
                <i class="u-earp lsthd_num">{{common.numberFormat(detailList.playCount)}}</i>
            </view>
            <view class="plhead_fr">
                <h2 class="f-thide2 f-brk lsthd_title">{{detailList.name}}</h2>
                <view class="lsthd_auth f-thide">
                    <a class="lsthd_link" href="">
                        <view class="u-avatar lsthd_ava">
                        <image class="u-img" src="{{detailList.creator.avatarUrl}}"></image>
                        <span class="ava-icon ava-icon-daren"></span>
            
                        </view>
                {{detailList.creator.nickname}}
                    </a>
                </view>
            </view>
        </view>
    </view>
    <h3 class="u-smtitle">歌曲列表</h3>
    <view class="m-sglst"wx:for="{{detailList.tracks}}" wx:key="index">
    <!-- <view class="m-list"wx:for="{{item.tracks}}" wx:for-item="item" wx:key="index"> -->
    <navigator class="m-sgitem"url="../musicplay/musicplay?id={{item.id}}&title={{item.name}}">
            <view class="sgfl">{{index+1}}</view>
            <view class="sgfr f-bd f-bd-btm">
                <view class="sgchfl">
                    <view class="f-thide sgtl">{{item.name}}
                <span class="sgalia" wx:if="{{item.alia.length}}">(<!-- -->{{item.alia}}<!-- -->)</span>
                </view>
                    <view class="f-thide sginfo">{{item.ar[0].name}}-{{item.al.name}}</view>
                </view>
                <view class="sgchfr">
                    <span class="u-hmsprt sgchply">
                    </span>
                </view>
            </view>
    </navigator> 
     <!-- </view> -->
    </view>
    listdetail页面渲染
    .plhead_wrap {
        display: flex;
        position: relative;
        z-index: 2;
    }
    .u-plhead {
        position: relative;
        padding: 30px 10px 30px 15px;
        overflow: hidden;
    }
    .u-plhead .plhead_bg {
        background-repeat: no-repeat;
        background-size: cover;
        background-position: 50%;
        -webkit-filter: blur(20px);
        filter: blur(20px);
        -webkit-transform: scale(1.5);
        -ms-transform: scale(1.5);
        transform: scale(1.5);
    }
    .u-plhead .plhead_bg, .u-plhead .plhead_bg:after {
        position: absolute;
        left: 0;
        top: 0;
        right: 0;
        bottom: 0;
        z-index: 1;
    }
    .u-plhead .plhead_bg:after {
        content: " ";
        background-color: rgba(0,0,0,.25);
    }
    .pylst_header .lsthd_title {
        padding-top: 1px;
        font-size: 17px;
        line-height: 1.3;
        color: #fefefe;
        height: 44px;
        display: -webkit-box;
        -webkit-box-pack: center;
    }
    .plhead_fl {
        position: relative;
        width: 114px;
        height: 114px;
        background-color: #e2e2e3;
    }
    image.u-img {
        width: 100%;
        height: 100%;
    }
    .pylst_header .lsthd_icon {
        position: absolute;
        z-index: 3;
        top: 10px;
        left: 0;
        padding: 0 8px;
        height: 17px;
        color: #fff;
        font-size: 9px;
        text-align: center;
        line-height: 17px;
        background-color: rgba(217,48,48,.8);
        border-top-right-radius: 17px;
        border-bottom-right-radius: 17px;
    }
    .u-plhead .plhead_fr {
        -webkit-box-flex: 1;
        -webkit-flex: 1 1 auto;
        -ms-flex: 1 1 auto;
        flex: 1 1 auto;
        width: 1%;
        margin-left: 16px;
    }
    .pylst_header .lsthd_ava {
        display: inline-block;
        width: 30px;
        height: 30px;
        border-radius: 50%;
        vertical-align: middle;
        margin-right: 5px;
    }
    .u-avatar {
        position: relative;}
    .u-avatar>.u-img {
        border-radius: 50%;
        
    }
    .u-avatar .ava-icon.ava-icon-daren {
        background-position: -40px 0;
    }
    .u-avatar .ava-icon {
        position: absolute;
        right: -5px;
        bottom: 0;
        width: 12px;
        height: 12px;
        background-image: url(//s3.music.126.net/mobile-new/img/usericn_2x.png?6423c06…=);
        background-repeat: no-repeat;
        background-size: 75px auto;
    }
    .pylst_header .lsthd_auth {
        display: block;
        position: relative;
        margin-top: 20px;
    }
     .lsthd_link {
        display: inline-block;
        color: hsla(0,0%,100%,.7);
    }
    .f-thide {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        word-break: normal;
    }
    .pylst_header .lsthd_num {
        position: absolute;
        right: 2px;
        top: 0;
        z-index: 3;
        padding-left: 15px;
        color: #fff;
        font-size: 12px;
        background-position: 0;
        background-repeat: no-repeat;
        background-size: 11px 10px;
        text-shadow: 1px 0 0 rgba(0,0,0,.15);
    }
     .lsthd_fl:after {
        content: " ";
        position: absolute;
        left: 0;
        top: 0;
        width: 100%;
        height: 18px;
        z-index: 2;
        background-image: -webkit-linear-gradient(left,rgba(0,0,0,0),rgba(0,0,0,.2));
        background-image: linear-gradient(90deg,rgba(0,0,0,0),rgba(0,0,0,.2));
    }
    .u-earp {
        background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAABfUlEQVR4AZXSA0y/YRAH8PvbdhqyrbnmMDPXFIYwZmt2bo5jDRlDtu1mZHc9z737/Z7t4n833ffu8+p5Af+zePAZQ7AaB3AF57Ebq0X35X7wBhNxEXdZLWKSmNwB/mATW1XVjH84+IdD2nC/tyclLuC9u5l3QmBXyn6vlorpPwXkw7TK+HqjJ+WVG7iqeuXWlXK9QaQV3ygQQ+ubFVFiyRp+gRjBc3gLP0XnWh11vUkkRgc+45wMetPBBX7xzyiQS28agTmxSSBYtof9X9zg051f/vMXt8N+IqEErqpl05/Dr64KfvVnyx2xKcHFsGyKA+DZveBZob/cuRgigNui2YE/D/4Qf2hrm4D2peHdg+CdtoUKoC+OifLhq/pcgWt6JBylYIADfa4eabH0amW5jGJRHOjypfKrlaUyAuAif4LHgParaMAYnMHkMUBbxgh8wAHPFTin/+l0ggOWK9AYdzZzPl8bwwDPFQBHei17DlTOwVdwEMMvHPAc8D/rBs0Gmx5Y/w/0AAAAAElFTkSuQmCC');
    }
    .m-sgitem, .m-sgitem .sgfl {
        display: -webkit-box;
        display: -webkit-flex;
        display: -ms-flexbox;
        display: flex;
    }
    .m-sgitem {
        padding-left: 10px;
    }
    .m-sgitem .sgfr {
        display: -webkit-box;
        display: -webkit-flex;
        display: -ms-flexbox;
        display: flex;
        position: relative;
    }
    .m-sgitem .sgfl {
        -webkit-box-align: center;
        -webkit-align-items: center;
        -ms-flex-align: center;
        align-items: center;
        -webkit-box-pack: center;
        -webkit-justify-content: center;
        -ms-flex-pack: center;
        justify-content: center;
        width: 40px;
        font-size: 17px;
        color: #999;
        margin-left: -10px;
    }
    .m-sgitem .sgchfl {
        padding: 6px 0;
        width: 0;
    }
    .m-sgitem .sgtl {
        font-size: 17px;
    }
    .m-sgitem .sginfo {
        font-size: 12px;
        color: #888;
    }
    .m-sgitem .sgchply {
        display: inline-block;
        width: 22px;
        height: 22px;
        background-position: -24px 0;
    }
    .u-hmsprt {
        background: url(//s3.music.126.net/mobile-new/img/index_icon_2x.png?5207a28…=) no-repeat;
        background-size: 166px 97px;
    }
    .m-sgitem .sgchfl, .m-sgitem .sgfr {
        -webkit-box-flex: 1;
        -webkit-flex: 1 1 auto;
        -ms-flex: 1 1 auto;
        flex: 1 1 auto;
    }
    .m-sgitem .sgchfr {
        display: -webkit-box;
        display: -webkit-flex;
        display: -ms-flexbox;
        -webkit-box-align: center;
        -webkit-align-items: center;
        -ms-flex-align: center;
        align-items: center;
        padding: 0 10px;
    }
    .m-sgitem .sgalia {
        color: #888;
        margin-left: 4px;
    }
    .u-smtitle {
        height: 23px;
        line-height: 23px;
        padding: 0 10px;
        font-size: 12px;
        color: #666;
        background-color: #eeeff0;
    }
    listdetail页面样式

    下一章介绍歌曲播放页面(唱片旋转,歌曲进度条,以及歌词滚动等效果)

  • 相关阅读:
    web服务器
    javascript对象属性为空的判断
    字符编码:ASCII,Unicode和UTF-8
    javascript 中英文字符长度和截断处理
    函数节流与去抖
    互联网协议
    解读 v8 排序源码
    乱序
    递归
    函数记忆
  • 原文地址:https://www.cnblogs.com/cheryshi/p/14780833.html
Copyright © 2011-2022 走看看