仿网易云音乐-微信小程序开发
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,主页面色,以及路由设计
若需要在小程序切后台后继续播放音频,需要在 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>
渲染歌单页面
推荐歌单页面有一个播放量转换


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值跳转到相对应的列表页面


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页面样式
下一章介绍歌曲播放页面(唱片旋转,歌曲进度条,以及歌词滚动等效果)
仿网易云音乐-微信小程序开发的更多相关文章
- [干货教程]仿网易云课堂微信小程序开发实战经验
本篇文章想跟大家分享下:我们公司“湖北诚万兴科技”最近刚帮客户定制开发.目前已上线的“哎咆课堂”微信小程序的开发经验分享.首先大概介绍下这个小程序所涉及到的主要技术点:微信登录.微信支付.微信小程序F ...
- [Chat]实战:仿网易云课堂微信小程序开发核心技术剖析和经验分享
本Chat以一个我参与开发并已上线运营近2年——类似网易云课堂的微信小程序项目,来进行微信小程序高级开发的学习. 本场Chat围绕项目开发核心技术分析,帮助你快速掌握在线视频.音频类小程序开发所需要的 ...
- 微信小程序开发日记——高仿知乎日报(下)
本人对知乎日报是情有独钟,看我的博客和github就知道了,写了几个不同技术类型的知乎日报APP 要做微信小程序首先要对html,css,js有一定的基础,还有对微信小程序的API也要非常熟悉 我将该 ...
- 微信小程序开发日记——高仿知乎日报(中)
本人对知乎日报是情有独钟,看我的博客和github就知道了,写了几个不同技术类型的知乎日报APP要做微信小程序首先要对html,css,js有一定的基础,还有对微信小程序的API也要非常熟悉 我将该教 ...
- 微信小程序开发日记——高仿知乎日报(上)
本人对知乎日报是情有独钟,看我的博客和github就知道了,写了几个不同技术类型的知乎日报APP 要做微信小程序首先要对html,css,js有一定的基础,还有对微信小程序的API也要非常熟悉 我将该 ...
- 《云阅》一个仿网易云音乐UI,使用Gank.Io及豆瓣Api开发的开源项目
CloudReader 一款基于网易云音乐UI,使用GankIo及豆瓣api开发的符合Google Material Desgin阅读类的开源项目.项目采取的是Retrofit + RxJava + ...
- 微信小程序开发学习资料
作者:初雪链接:https://www.zhihu.com/question/50907897/answer/128494332来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明 ...
- 微信小程序开发中的二三事之网易云信IMSDK DEMO
本文由作者邹永胜授权网易云社区发布. 简介 为了更好的展示我们即时通讯SDK强悍的能力,网易云信IM SDK微信小程序DEMO的开发就提上了日程.用产品的话说就是: 云信 IM 小程序 SDK 的能力 ...
- 我们的微信小程序开发
基于微信小程序的系统开发准备工作 腾讯推出微信小程序也有一段时间了,在各种行业里面也都掀起一阵阵的热潮,很多APP应用被简化为小程序的功能迅速推出,同时也根据小程序的特性推出各种独具匠心的应用,相对传 ...
- C# WPF 低仿网易云音乐(PC)歌词控件
原文:C# WPF 低仿网易云音乐(PC)歌词控件 提醒:本篇博客记录了修改的过程,废话比较多,需要项目源码和看演示效果的直接拉到文章最底部~ 网易云音乐获取歌词的api地址 http://music ...
随机推荐
- Hadoop_08 Hadoop重装
今日总结: 由于之前配置存在问题,今天学到后面之后发现很多bug,需要全部卸载重新配置Hadoop.ZooKeeper以及Hbase.
- .NET Core WebApi 多语言本地化,动态切换多语言
.NET Core WebApi 多语言本地化,动态切换多语言 原生的.net core webapi 动态多语言本地话 具体更多详细内容,可以参考官方文档 首先看效果图 整体项目结构图 开始前需要讲 ...
- C#数据去重的5种方式,你知道几种?
前言 今天我们一起来讨论一下关于C#数据去重的的5种方式,每种方法都有其特点和适用场景,我们根据具体需求选择最合适的方式.当然欢迎你在评论区留下你觉得更好的C#数据去重的方式. 使用HashSet去重 ...
- #计数,记忆化搜索#C 连边方案
分析 设\(dp[i][j][k][l]\)表示处理到\([i-l+1,i]\)的连边,二进制状态(奇点还是偶点)为\(k\)的方案数, 最后一维是为了避免算重,那么如果第\(i-l+1\)位是偶点可 ...
- 使用 Debian、Docker 和 Nginx 部署 Web 应用
前言 本文将介绍基于 Debian 的系统上使用 Docker 和 Nginx 进行 Web 应用部署的过程.着重介绍了 Debian.Docker 和 Nginx 的安装和配置. 第 1 步:更新和 ...
- 深入理解java的泛型
目录 简介 泛型和协变 泛型在使用中会遇到的问题 类型擦除要注意的事项 总结 简介 泛型是JDK 5引入的概念,泛型的引入主要是为了保证java中类型的安全性,有点像C++中的模板. 但是Java为了 ...
- 战码先锋直播预告丨参与文档贡献,开启OpenHarmony社区贡献之旅
OpenAtom OpenHarmony(以下简称"OpenHarmony")工作委员会首度发起「OpenHarmony开源贡献者计划」(本期OpenHarmony开源贡献者计划以 ...
- OpenHarmony技术挑战课题征集
OpenHarmony技术挑战课题征集 OpenAtom OpenHarmony(以下简称"OpenHarmony")是由开放原子开源基金会(OpenAtom Foundation ...
- 初学STM32 SDIO(一)
1. SDIO协议简介 SDIO全称是安全数字输入/输出接口 ,控制器对SD卡进行读写通信操作一般有两种通信接口可选,一种是SPI接口,另外一种就是SDIO接口. 多媒体卡(MMC).SD卡. S ...
- 使用谷歌浏览器打开PDF文件,怎么关闭缩略图
我们在使用谷歌浏览器浏览PDF文件时,总是会出现章节预览缩略图和工具栏,我们可以使用 参数来控制浏览器不显示出工具栏 #scrollbars=0&toolbar=0&statusbar ...