仿网易云音乐-微信小程序开发

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页面样式

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

 

posted @ 2021-05-18 14:33  cheryshi  阅读(1791)  评论(0编辑  收藏  举报