网易云音乐小程序案例分享 附完整代码
todo:
添加音乐到收藏(最近)列表
歌词滚动
从一个 hello world 开始
微信开发者工具生成 目录如下:
.
|-- app.js
|-- app.json
|-- app.wxss
|-- pages
| |-- index # 主页
| | |-- index.js
| | |-- index.json
| | |-- index.wxml
| | `-- index.wxss
| `-- log # 日志页面
| | |-- log.js
| | |-- log.json
| | |-- log.wxml
| | `-- log.wxss
`-- utils # 工具`-- util.js
大体为: 每一个 page 即是一个页面文件 ,每个页面有一个 js/wxml/wxss/json 文件 规定:描述页面的这四个文件必须具有相同的路径与文件名。
全局下同路,为公共的逻辑,样式,配置
与 HTML 不同:用 view text navigator 代替 div span a
开发者文档走马观花
app.json: 注册 pages window tabBar networkTimeout
组件说明
.js: 作为逻辑层 与 wxml 交互 有着丰富的 网络, 媒体, 文件, 数据缓存, 位置, 设备, 界面...的 API
官方文档
.wxml: 数据驱动的视图层 + 微信提供了大量的组件 表单 导航 媒体 ...
官方组件不够,weui 来凑
weui 为小程序提供了 weui.wxcss 但大多是造官方组件的轮子
这里精选,也算是补充两个常用组件
对于小程序没有 DOM 操作 不熟悉 mvvm 思想的同学 是个很好的入门
navbar
<!-- wxml --><viewclass="weui-tab"><viewclass="weui-navbar"><blockwx:for="{{tabs}}"wx:key="*this"><viewid="{{index}}"class="weui-navbar__item {{activeIndex == index ? 'weui-bar__item_on' : ''}}"bindtap="tabClick"><viewclass="weui-navbar__title">{{item}}</view></view></block><viewclass="weui-navbar__slider"style="left: {{sliderLeft}}px; transform: translateX({{sliderOffset}}px); -webkit-transform: translateX({{sliderOffset}}px);"></view></view><viewclass="weui-tab__panel"><viewclass="weui-tab__content"hidden="{{activeIndex != 0}}">选项一的内容</view><viewclass="weui-tab__content"hidden="{{activeIndex != 1}}">选项二的内容</view><viewclass="weui-tab__content"hidden="{{activeIndex != 2}}">选项三的内容</view></view></view>
block 渲染 data 里面的四个 tabs,slider 为激活 tab 选项时候的表现,panel 为内容面板
//js
varsliderWidth=96;// 需要设置slider的宽度,用于计算中间位置
Page({data:{tabs:["选项一","选项二","选项三"],activeIndex:1,sliderOffset:0,sliderLeft:0},onLoad:function(){varthat=this;wx.getSystemInfo({success:function(res){that.setData({sliderLeft:(res.windowWidth/that.data.tabs.length-sliderWidth)/2,sliderOffset:res.windowWidth/that.data.tabs.length*that.data.activeIndex});}});},tabClick:function(e){this.setData({sliderOffset:e.currentTarget.offsetLeft,activeIndex:e.currentTarget.id});}});
了解 mvvm 思想的同学不难看出 通过 tabs 数组渲染出来选项后每次点击获取 id 然后通过设置 hidden 显示或隐藏
searchbar
<viewclass="weui-search-bar"><viewclass="weui-search-bar__form"><viewclass="weui-search-bar__box"><iconclass="weui-icon-search_in-box"type="search"size="14"></icon><inputtype="text"class="weui-search-bar__input"placeholder="搜索"value="{{inputVal}}"focus="{{inputShowed}}"bindinput="inputTyping"/><viewclass="weui-icon-clear"wx:if="{{inputVal.length > 0}}"bindtap="clearInput"><icontype="clear"size="14"></icon></view></view><labelclass="weui-search-bar__label"hidden="{{inputShowed}}"bindtap="showInput"><iconclass="weui-icon-search"type="search"size="14"></icon><viewclass="weui-search-bar__text">搜索</view></label></view><viewclass="weui-search-bar__cancel-btn"hidden="{{!inputShowed}}"bindtap="hideInput">取消</view></view><viewclass="weui-cells searchbar-result"wx:if="{{inputVal.length > 0}}"><navigatorurl=""class="weui-cell"hover-class="weui-cell_active"><viewclass="weui-cell__bd"><view>实时搜索文本</view></view></navigator></view>
一个 input 输入框 + 一个搜索 label+ 一个清楚内容的 icon + 取消按钮
Page({data:{inputShowed:false,inputVal:""},showInput:function(){this.setData({inputShowed:true});},hideInput:function(){this.setData({inputVal:"",inputShowed:false});},clearInput:function(){this.setData({inputVal:""});},inputTyping:function(e){this.setData({inputVal:e.detail.value});}});
input 上面有一层 label 通过 Page 里面状态的改变而操作其 wxml 状态的改变
不难体会到:小程序和 Vue 的思想还是挺接近的
站在巨人的肩膀上为大佬们提供云音乐 API
---获取云音乐api
巨人的源 github 项目
在此我将他部署到 leancloud 上
即可在线访问,免去烦人的本地 localhost 启动,在线 url
http://neteasemusic.leanapp.cn
调用例子:
http://neteasemusic.leanapp.cn/search?keywords=海阔天空
http://neteasemusic.leanapp.cn/lyric?id=347230
具体参考 API
详细文档
一切具备 只欠东风
生成目录
.
|-- app.js
|-- app.json
|-- app.wxss
|-- common.js #公用js
|-- images #存放项目图片
|-- style
| |-- weui.wxss # 引入weui样式 万一你自己不想写css样式呢
|-- pages
| |-- find # 发现音乐
| | |-- index.js
| | |-- index.json
| | |-- index.wxml
| | `-- index.wxss
| |--my # 我的音乐
| | |-- index.js
| | |-- index.json
| | |-- index.wxml
| | `-- index.wxss
| |--now # 正在播放
| | |-- index.js
| | |-- index.json
| | |-- index.wxml
| | `-- index.wxss
| |--account # 账号
| | |-- index.js
| | |-- index.json
| | |-- index.wxml
| | `-- index.wxss
| |-- index # 主页
| | |-- index.js
| | |-- index.json
| | |-- index.wxml
| | `-- index.wxss
| `-- log # 日志页面
`-- utils # 工具`-- util.js
请先在在 app.json 中注册页面,设置 navigation,配置 tabbar
{"pages":["pages/find/index","pages/my/index","pages/now/index","pages/account/index","pages/index/index"],"window":{"backgroundTextStyle":"light","navigationBarBackgroundColor":"#D43C33","navigationBarTitleText":"网易云音乐","navigationBarTextStyle":"white","backgroundColor":"#FBFCFD"},"tabBar":{"backgroundColor":"#2A2C2E","color":"#a7a7a7","selectedColor":"#ffffff","list":[{"iconPath":"./images/find.png","selectedIconPath":"./images/find1.png","pagePath":"pages/find/index","text":"发现音乐"},{"iconPath":"./images/my.png","selectedIconPath":"./images/my1.png","pagePath":"pages/my/index","text":"我的音乐"},{"iconPath":"./images/now.png","selectedIconPath":"./images/now1.png","pagePath":"pages/now/index","text":"正在播放"},{"iconPath":"./images/account.png","selectedIconPath":"./images/account1.png","pagePath":"pages/account/index","text":"账号"}]}}
发现音乐
布局分为搜索框,navbar,swiper 滑动,三列,以及两行三列构成
tips:小程序中 flex 布局基本无兼容性问题 ,可大胆使用
前三个可用上文提到的组件和小程序 swiper 组件快速完成,
对于搜索功能
我们在搜索 input 上绑定一个 inputTyping 事件,这样每次键入完毕都可以得到结果,然后我们直接请求 API
//index.js
//获取应用实例
// 个人网易云音乐 ID 66919655
varapp=getApp()Page({data:{searchReault:[]},//绑定事件
inputTyping:function(e){letthat=thisconsole.log(e.detail)this.setData({inputVal:e.detail.value});wx.request({url:'http://neteasemusic.leanapp.cn/search',data:{keywords:e.detail.value},method:'GET',success:function(res){lettemp=[]if(!res.data.result.songs){return;}//遍历数据
res.data.result.songs.forEach((song,index)=>{temp.push({id:song.id,name:song.name,mp3Url:song.mp3Url,picUrl:song.album.picUrl,singer:song.artists[0].name})//设置数据
that.setData({searchReault:temp})})// 存入搜索的结果进缓存
wx.setStorage({key:"searchReault",data:temp})}})}});
data 里面的 searchReault 数组存入搜索结果,发起一个 wx.request,用 GET 方式传入参数,组织好 JSON 后设置 data,然后将搜索结果存入本地缓存
wxml 渲染 searchReault:
并且自定义 data 属性,navigator 的打开方式为 tab 切换 open-type="switchTab" ,绑定一个 tonow 事件 bindtap="tonow"
<blockwx:for="{{searchReault}}"wx:key="item"style="overflow-y: scroll;"><navigatorurl="../now/index"class="weui-cell"hover-class="weui-cell_active"data-id="{{item.id}}"data-name="{{item.name}}"data-songUrl="{{item.mp3Url}}"data-picUrl="{{item.picUrl}}"data-singer="{{item.singer}}"open-type="switchTab"bindtap="tonow"><viewclass="weui-cell__bd"><viewclass="song-name">{{item.name}}<textclass="song-singer">{{item.singer}}</text></view></view></navigator></block>
在 tonow 事件中,获取当前的歌曲
tonow: function (event) {let songData = {id: event.currentTarget.dataset.id,name: event.currentTarget.dataset.name,mp3Url: event.currentTarget.dataset.songurl,picUrl: event.currentTarget.dataset.picurl,singer: event.currentTarget.dataset.singer}// 将当前点击的歌曲保存在缓存中wx.setStorageSync('clickdata', songData)wx.switchTab({url: '../now/index'})}
正在播放
布局:歌曲封面,滑动条上下为操作按钮, 封面在采用圆角,rotate,transition 既可以 滑动快进:在滑动条上绑定事件 slider3change
//滑动 歌曲快进
functionsliderToseek(e,cb){wx.getBackgroundAudioPlayerState({success:function(res){vardataUrl=res.dataUrlvarduration=res.durationletval=e.detail.valueletcal=val*duration/100cb&&cb(dataUrl,cal);}})}//分隔 在page中调用
slider3change:function(e){sliderToseek(e,function(dataUrl,cal){wx.playBackgroundAudio({dataUrl:dataUrl})wx.seekBackgroundAudio({position:cal})})},
一个自定义的 sliderToseek 函数:
参数 e 可以获取滑动的值,获取正在播放的音乐信息成功后执行 回调函数1->播放 回调函数2->跳到指定位置; 拆分歌词: 在 API 中得到的歌词:"[00:00.00] 作曲 : 黄家驹 [00:01.00] 作词 : 黄家驹 [00:18.580]今天我 寒夜里看雪飘过 [00:25.050]怀着冷却了的心窝漂远方 [00:30.990]风雨里追赶 " 在 page 外定义函数:
以 ] 划分数组 第二部分就是歌词内容:item.split(']')[1] 第一部分即为对应的时间:item.split(']')[0]
// 获取歌词
functiongetlyric(id,cb){console.log('id:',id)leturl=`http://neteasemusic.leanapp.cn/lyric`wx.request({url:url,data:{id:id},method:'GET',// OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT
// header: {}, // 设置请求的 header
success:function(res){// success
if(!res.data.lrc.lyric)returnfalse;letlyric=res.data.lrc.lyriclettimearr=lyric.split('[')letobj={}letlyricArr=[]// seek 为键 歌词为value
timearr.forEach((item)=>{letkey=parseInt(item.split(']')[0].split(':')[0])*60+parseInt(item.split(']')[0].split(':')[1])letval=item.split(']')[1]obj[key]=val})for(letkeyinobj){// obj[key] = obj[key].split('\n')[0]
lyricArr.push(obj[key])}cb&&cb(obj,lyricArr)},fail:function(res){// fail
},complete:function(res){// complete
}})}
在 page 中调用:传入歌曲 ID(上文我们已经存入缓存,在缓存中取出即可),和将其设置在 data 的回调
getlyric(id,function(data,lyricArr){that.setData({lyricobj:data,lyricArr:lyricArr})})
wxml 进行渲染:
<!--歌词--><viewclass="lyric-content"hidden="{{islyric}}"style="height:401px; overflow-y: scroll;"bindtap="showCircle"><viewclass="lyric"style="overflow-y: scroll;"><blockwx:for="{{lyricArr}}"><view> {{item}} </view></block></view></view>
添加歌曲:
我的可以在本地缓存中添加两个 key 入对应的信息
like:我的喜欢
recent:最近
选择事件
radioChange:function(e){console.log('radio发生change事件,携带value值为:',e.detail.value)this.setData({percent:'100%'})},//radio发生change事件,携带value值为: like
//radio发生change事件,携带value值为: recent
点击添加按钮,向上呼出选项,将当前播放的歌曲设置到对应的数组即可
进行当前歌曲的播放: 页面 onshow 的时候,获取本地缓存的信息,在 success 的回调中,设置到 data,以供页面解析,而后在获取歌词的函数中也进行一次回调,设置歌词, 播放本地音乐,播放成功之后,在 success 的回调中,获取正在播放的音乐信息,包括该歌曲的总时长,再进行设置。
onShow: function () {var that = this;console.log('正在播放 is on show')// 获取缓存wx.getStorage({key: 'clickdata',success: function (res) {var value = res.datavar id = value.idif (value) {// 设置到datathat.setData({id:id,name: value.name,src: value.mp3Url,poster: value.picUrl,author: value.singer})getlyric(id,function(data, lyricArr){that.setData({lyricobj:data,lyricArr:lyricArr})}) }let url = that.data.src || value.mp3Url;// 播放wx.playBackgroundAudio({dataUrl: value.mp3Url,title: value.name,coverImgUrl: value.picUrl,success: function () {wx.hideLoading()console.log('url',url)setTimeout(function(){wx.getBackgroundAudioPlayerState({success: function (res) {var tempduration = res.durationconsole.log('get bg success', tempduration, res)// 设置时长that.setData({sumduration: tempduration})},complete: function (res) {console.log(' get bg complete:', res)}})},1000)},complete:function(){// 获取正在播放的信息console.log('play',url)}})}})},
这样我们不知不觉进入多个回调嵌套的问题
代码优化,使用 Promise,较为优雅地解决回调
小程序暂时不支持 async await
在 common.js 中为小程序提供的 API 上裹上一层 Promise,并且通过 module.exports = operation 暴露出去
const operation = {getMusicData: function () {return new Promise((resolve, reject) => {wx.getBackgroundAudioPlayerState({success: function (res) {resolve(res);},fail: function (err) {reject(err);}})})},// 播放音乐 参数:url title 图片urlplayMusic: function (url, title, pic) {return new Promise((resolve, reject) => {wx.playBackgroundAudio({dataUrl: url,title: title,coverImgUrl: pic,success: function () {resolve(true)},fail: function () {reject(new Error('播放错误'));}})})},asyncGetStorage: function (key) {return new Promise((resolve, reject) => {wx.getStorage({key: key,success: function (res) {resolve(res.data)},fail: function (err) {reject(err)}})})},getlyric: function (id) {return new Promise((resolve, reject) => {console.log('id:', id)let url = `http://neteasemusic.leanapp.cn/lyric`wx.request({url: url,data: {id: id},method: 'GET', // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT// header: {}, // 设置请求的 headersuccess: function (res) {// successif (!res.data.lrc.lyric) return false;let lyric = res.data.lrc.lyriclet timearr = lyric.split('[')let obj = {}let lyricArr = []// seek 为键 歌词为valuetimearr.forEach((item) => {let key = parseInt(item.split(']')[0].split(':')[0]) * 60 + parseInt(item.split(']')[0].split(':')[1])let val = item.split(']')[1]obj[key] = val})for (let key in obj) {// obj[key] = obj[key].split('\n')[0]lyricArr.push(obj[key])}// cb && cb(obj, lyricArr)resolve(lyricArr)},fail: function (err) {reject(err)},complete: function (res) {// complete}})})}
}
module.exports = operation
重写一下当前歌曲播放事件
onShow: function () {let that = this;Common.asyncGetStorage('clickdata')//本地缓存.then(data => {// console.log(data)if (!data) return;that.setData({id: data.id,name: data.name,src: data.mp3Url,poster: data.picUrl,author: data.singer})return Common.playMusic(data.mp3Url, data.name, data.picUrl);}).then(status => {if(!status) return;wx.hideLoading();console.log('id,',that.data.id)return Common.getlyric(that.data.id)}).then((lyricArr) => {console.log('lyricArr',lyricArr)that.setData({lyricArr: lyricArr})return Common.getMusicData()}).then(data => {let tempduration = data.durationconsole.log('get bg success', tempduration, data)// 设置时长that.setData({sumduration: tempduration})})},
这样即可缩减部分代码.
完整代码:https://download.csdn.net/download/weixin_55771290/87402443
网易云音乐小程序案例分享 附完整代码相关推荐
- 网易云音乐小程序,带后台(SpringBoot)
目录 1.简介 2.技术栈 3.环境 4.项目后台配置 5.项目展示 6.下载地址 1.简介 此系统是仿网易云音乐 网易云音乐是一款专注于发现与分享的音乐产品,依托专业音乐人.DJ.好友推荐及社交功能 ...
- taro 重新加载小程序_taro-music一款开源网易云音乐小程序
简介 taro-music 是基于taro + taro-ui + redux + react-hooks + typescript 开发的网易云音乐小程序,目前正在使用react-hooks重构中. ...
- 简单的仿网易云音乐小程序(总结)
简单的仿网易云音乐小程序(总结) 这个小程序学到了以下知识点 配置网络请求 进行网络请求 进行音乐播放 进行模版使用 各种页面触发事件 输入框的使用 简单的自定义tab页面切换 导航的数据传输
- 简单的仿网易云音乐小程序(一)
简单的仿网易云音乐小程序(一) 前言 思维图 注意事项 虚拟机调试 真机调试 主页面 搜索框 歌单列表 歌单模版 wxs filter 页面逻辑 等待搜索页面 搜索框 clearValue start ...
- 如何用 Python 爬取网易云音乐的 10w+ 评论?附详细代码解读
在简单学习了Python爬虫之后,我的下一个目标就是网易云音乐.因为本人平时就是用它听的歌,也喜欢看歌里的评论,所以本文就来爬一爬网易云音乐的评论吧! 正式进入主题 首先是找到目标网页并分析网页结构, ...
- 网易云音乐小程序登录接口显示400,拥挤问题解决
有在做网易云登录接口的小伙伴会发现自己会出现如下问题 解决办法: 用这个接口:https://neteasecloudmusicapi.js.org/#/ 配置好,先cmd,再npm i,如果出现np ...
- 网易云音乐小程序 笔记
小程序750px html 双引号 js 单引号 flex布局:弹性盒子 授权动作只发生一次 获取用户信息 flex-direction: column; // 修改flex主轴x修改为y lign- ...
- 网易云音乐排行榜接口取消后解决方法(网易云音乐小程序)
解决办法: 结合"所有榜单"和"获取歌单详情"两个api获取.因为获取歌单详情api只能获取单个歌单的内容,所以要先获得多个歌单id. 完整代码: let to ...
- netease-im网易云通信小程序集成实践+群组功能完善
在微信里放一个IM,被指定放网易云通信.这次实践是一场非常虐心的体验,虽然集成网易云通信有官方资料参考,也有官方的demo参考,但是踩的坑也不少. 一.不完全是技术问题 消息漫游需要联系商务开通 二. ...
最新文章
- apktoolkit apk反编译没有文件_重新编译mono——修改apk中Assembly-CSharp.dll并重新打包...
- 从中心走向边缘——深度解析云原生边缘计算落地痛点
- rest-framework之响应器(渲染器)
- ElasticSearch入门 —— 集群搭建
- 银行家算法实验报告c语言版,银行家算法实验报告C语言版.doc
- python---基础知识
- Android Lint简介
- 万用表二极管档和三极管档的使用
- 自己怎么开发一个软件app、如何开发一个app系统软件?
- c语言求圆锥的表面积和体积_c语言如何编程求圆体积和表面积
- 五大方面多管齐下,用友助力企业建设世界一流司库体系
- github国内镜像
- 解决Nginx出现 403 Forbidden的办法
- 圖譜謎宮(2019年6月28日於鄂爾多斯)
- C语言入门part4—大致梳理最终篇
- 【快乐手撕LeetCode题解系列】——消失的数字
- html+css+js制作美团界面
- 弹出式网络广告价值分析案例
- java jdk8 使用stream实现两个list集合合并成一个list集合(对象属性的合并)
- 计算机网络(第八版 谢希仁著)(上)
热门文章
- 12月21诛仙服务器维护,诛仙手游正式服12月22日例行更新维护公告
- go-cqhttp发送本地图片
- 3600000毫秒等于多少小时_工地扬尘监测规定-多少算超标?
- 万卷书- 创新型学校 [Creative Schools]
- UML建模工具Astah Pro教程
- 计算机图像技术在医学上的应用,计算机图像处理技术在医学中的应用
- 区块链技术加持下的十款智能硬件产(kuang)品(ji)
- 电子邮箱格式怎么写?你知道电子邮箱格式都有哪些吗?
- Flash中使用Filereference上传文件的一些注意事项
- Newtonsoft.Json取json字符串中的值得用法 这里是取的时候