当前位置:   article > 正文

码农武:网易云音乐微信小程序开发流程

码农武:网易云音乐微信小程序开发流程
  • 开发环境(微信/node.js环境)
  • 开发工具(微信开发者工具)
  • Node.js代理服务器
  • 网易云音乐开发接口API

项目介绍:

        网易云音乐界面

                

        数据渲染

                

                

                 真实接口请求的数据,数据渲染

        动态效果

                

        功能实现

                

                音乐播放,暂停,下一曲

项目实现:

        开发准备:

                注册成为微信小程序开发者

                微信公众平台

                下载微信开发者工具

                新建项目:

                        项目目录了解:

                        

                        components----组件文件夹

                        pages------------页面文件夹

                        utils--------------封装业务逻辑文件

                        app.js-----------全局功能实现文件(App对象,注册)

                        app.json--------全局配置文件

                        app.wxss-------全局样式设置

                安装Node.js环境

                

下载安装:

        测试:

        

        启动代理服务器:

        

             进入当前文件夹:

                

        进入命令行:

        

        注意:如果没有npm,需要先安装NODEMON

        服务器启动成功

        网易云音乐接口API

        

        https://binaryify.github.io/NeteaseCloudMusicApi/#/

      项目实现:

        首页搭建:

                pages新建首页:

                

                结构实现代码

                        头部:

                        

轮播图区域代码实现:

导航区域结构实现:

推荐歌曲区域结构

样式设置:

       

  1. /* pages/index/index.wxss */
  2. /* 搜索 */
  3. .header {
  4. display: flex;
  5. padding: 10rpx;
  6. }
  7. .header image{
  8. width: 60rpx;
  9. height: 60rpx;
  10. }
  11. .header .search {
  12. border: 1rpx solid #eee;
  13. margin: 0 10rpx;
  14. border-radius: 10rpx;
  15. font-size: 26rpx;
  16. text-align: center;
  17. line-height: 60rpx;
  18. color: #d43c33;
  19. /*flex-grow:可拉伸 flex-shrink:可压缩 flex-basic:当前元素的宽度*/
  20. /*flex: flex-grow:0,flex-shrink:1,flex-basic:auto*/
  21. /*flex:1 flex-grow:1,flex-shrink:1,flex-basic:0%*/
  22. /*flex:auto flex-grow:1,flex-shrink:1,flex-basic:auto*/
  23. flex: 1;
  24. }
  25. /* 轮播图 */
  26. .banners{
  27. width: 100%;
  28. height: 300rpx;
  29. }
  30. .banners image{
  31. width: 100%;
  32. height: 100%;
  33. }
  34. .navContainer{
  35. display: flex;
  36. }
  37. .navItem{
  38. display: flex;
  39. flex-direction: column;
  40. align-items: center;
  41. width: 20%;
  42. }
  43. /* 矢量图 */
  44. .navItem .iconfont{
  45. width: 100rpx;
  46. height: 100rpx;
  47. border-radius: 50%;
  48. text-align: center;
  49. line-height: 100rpx;
  50. background: rgb(240,19,19);
  51. font-size: 50rpx;
  52. color: #fff;
  53. margin: 20rpx 0;
  54. }
  55. .navItem text{
  56. font-size: 26rpx;
  57. }
  58. /* 推荐歌曲 */
  59. .recommendContainer{
  60. padding: 20rpx;
  61. }
  62. /* 内容推荐区 */
  63. .recommendScroll{
  64. display: flex;
  65. height: 270rpx;
  66. }
  67. .scrollItem{
  68. width: 200rpx;
  69. margin-right: 20rpx;
  70. }
  71. .scrollItem image{
  72. width: 200rpx;
  73. height: 200rpx;
  74. border-radius: 10rpx;
  75. }
  76. .scrollItem text{
  77. font-size: 24rpx;
  78. /* 多行文本溢出隐藏 省略号代替 */
  79. overflow: hidden;
  80. text-overflow: ellipsis;
  81. display: -webkit-box;
  82. -webkit-box-orient: vertical;/*设置纵向对齐*/
  83. -webkit-line-clamp: 2;/*设置盒子行数*/
  84. }
  85. /* 排行榜 */
  86. .topList{
  87. padding: 20rpx;
  88. }
  89. .topListSwiper{
  90. height: 440rpx;
  91. }
  92. .swiperItem{
  93. width: 95%;
  94. background-color: rgb(248, 248, 248);
  95. border-radius: 10rpx;
  96. }
  97. .swiperItem .title{
  98. font-size: 30rpx;
  99. line-height: 80rpx;
  100. padding-left: 10rpx;
  101. }
  102. .musicItem{
  103. /* 一个元素为flex,子元素自动变成block */
  104. display: flex;
  105. }
  106. .musicItem image{
  107. width: 100rpx;
  108. height: 100rpx;
  109. margin-bottom: 20rpx;
  110. border-radius: 6rpx;
  111. padding-left: 10rpx;
  112. }
  113. .musicItem .count{
  114. width: 100rpx;
  115. height: 100rpx;
  116. text-align: center;
  117. line-height: 100rpx;
  118. }
  119. .musicItem .musicName{
  120. height: 100rpx;
  121. line-height: 100rpx;
  122. max-width: 400rpx;
  123. white-space: nowrap;
  124. overflow: hidden;
  125. text-overflow: ellipsis;
  126. }

功能实现:

  1. // pages/index/index.js
  2. import request from '../../utils/request'
  3. Page({
  4. /**
  5. * 页面的初始数据
  6. */
  7. data: {
  8. bannerList:[],//轮播图数据
  9. recommendList:[],//推荐歌单数据
  10. topList:[],//排行榜数据
  11. },
  12. /**
  13. * 生命周期函数--监听页面加载
  14. */
  15. onLoad: async function (options) {
  16. //滑块数据
  17. let bannerListData = await request('/banner', {type:2});
  18. this.setData({
  19. bannerList: bannerListData.banners
  20. })
  21. //推荐歌单数据
  22. let recommendListData = await request('/personalized', {limit:10})
  23. this.setData({
  24. recommendList: recommendListData.result
  25. })
  26. //排行榜数据
  27. let index = 0;
  28. let resultArr = [];
  29. while(index<5){
  30. let topListData = await request('/top/list', {idx:index++});
  31. let topListItem = {name: topListData.playlist.name, tracks: topListData.playlist.tracks.slice(0, 3)};
  32. resultArr.push(topListItem);
  33. //拿到数据就渲染到页面,但是渲染次数多
  34. this.setData({
  35. topList: resultArr
  36. })
  37. }
  38. },
  39. //跳转到每日推荐歌曲页面
  40. toRecommendSong(){
  41. wx.navigateTo({
  42. url: '/pages/recommendSong/recommendSong',
  43. })
  44. },
  45. //跳转到搜索页面
  46. toSearch(){
  47. wx.navigateTo({
  48. url: '/pages/search/search',
  49. })
  50. },
  51. toSongDetail(event){
  52. wx.navigateTo({
  53. url: '/pages/songDetail/songDetail?song=' + event.currentTarget.id
  54. })
  55. },
  56. //跳转到歌单歌曲列表页面
  57. toPlayList(event){
  58. wx.navigateTo({
  59. url: '/pages/playlist/playlist?id=' + event.currentTarget.id
  60. })
  61. },
  62. /**
  63. * 生命周期函数--监听页面初次渲染完成
  64. */
  65. onReady: function () {
  66. },
  67. /**
  68. * 生命周期函数--监听页面显示
  69. */
  70. onShow: function () {
  71. },
  72. /**
  73. * 生命周期函数--监听页面隐藏
  74. */
  75. onHide: function () {
  76. },
  77. /**
  78. * 生命周期函数--监听页面卸载
  79. */
  80. onUnload: function () {
  81. },
  82. /**
  83. * 页面相关事件处理函数--监听用户下拉动作
  84. */
  85. onPullDownRefresh: function () {
  86. },
  87. /**
  88. * 页面上拉触底事件的处理函数
  89. */
  90. onReachBottom: function () {
  91. },
  92. /**
  93. * 用户点击右上角分享
  94. */
  95. onShareAppMessage: function () {
  96. }
  97. })

个人中心页面搭建:

        目录:

界面搭建代码:

  1. <!--pages/personal/personal.wxml-->
  2. <view class="personalContainer">
  3. <view class="user-section">
  4. <image class="bg" src="/static/images/personal/bgImg2.jpg"></image>
  5. <view class="user-info-box" bindtap="toLogin">
  6. <view class="portrait-box">
  7. <image class="portrait" src='{{userInfo.avatarUrl ? userInfo.avatarUrl : "/static/images/personal/missing-face.png"}}'></image>
  8. </view>
  9. <view class="info-box">
  10. <text class="username">{{userInfo.nickname ? userInfo.nickname : "游客"}}</text>
  11. </view>
  12. </view>
  13. <view class="vip-card-box">
  14. <image class="card-bg" src="/static/images/personal/vip-card-bg.png" mode=""></image>
  15. <view class="b-btn">
  16. 立即开通
  17. </view>
  18. <view class="tit">
  19. <!-- 会员图标-->
  20. <text class="iconfont icon-huiyuan"></text>
  21. 会员
  22. </view>
  23. <text class="e-m">manster union</text>
  24. <text class="e-b">开通会员听歌, 撸代码</text>
  25. </view>
  26. </view>
  27. <view
  28. class="cover-container"
  29. bindtouchstart="handleTouchStart"
  30. bindtouchmove="handleTouchMove"
  31. bindtouchend="handleTouchEnd"
  32. style="transform: {{coverTransform}};transition: {{coverTransition}}"
  33. >
  34. <image class="arc" src="/static/images/personal/arc.png"></image>
  35. <!-- 个人中心导航 -->
  36. <view class="nav-section">
  37. <view class="nav-item" hover-class="common-hover" hover-stay-time="50">
  38. <text class="iconfont icon-xiaoxi"></text>
  39. <text>我的消息</text>
  40. </view>
  41. <view class="nav-item" hover-class="common-hover" hover-stay-time="50">
  42. <text class="iconfont icon-haoyou"></text>
  43. <text>我的好友</text>
  44. </view>
  45. <view class="nav-item" hover-class="common-hover" hover-stay-time="50">
  46. <text class="iconfont icon-geren"></text>
  47. <text>个人主页</text>
  48. </view>
  49. <view class="nav-item" hover-class="common-hover" hover-stay-time="50">
  50. <text class="iconfont icon-gexingzhuangban"></text>
  51. <text>个性装扮</text>
  52. </view>
  53. </view>
  54. <!-- 个人中心列表 -->
  55. <view class="personalContent">
  56. <view class="recentPlayContainer">
  57. <text class="title">最近播放</text>
  58. <!-- 最近播放记录 -->
  59. <scroll-view wx:if="{{recentPlayList.length}}" scroll-x="true" class="recentScroll" enable-flex="true">
  60. <view class="recentItem" wx:for="{{recentPlayList}}" wx:key="id">
  61. <image src="{{item.song.al.picUrl}}"></image>
  62. </view>
  63. </scroll-view>
  64. <view wx:else>暂无播放记录</view>
  65. </view>
  66. <view class="cardList">
  67. <view class="card-item">
  68. <text class="title">我的音乐</text>
  69. <text class="more"> > </text>
  70. </view>
  71. <view class="card-item">
  72. <text class="title">我的收藏</text>
  73. <text class="more"> > </text>
  74. </view>
  75. <view class="card-item">
  76. <text class="title">我的电台</text>
  77. <text class="more"> > </text>
  78. </view>
  79. </view>
  80. </view>
  81. </view>
  82. <!-- 退出登录按钮 -->
  83. <view hidden="{{!isLogin}}">
  84. <button class="logout-btn" bindtap="logout">退出登录</button>
  85. </view>
  86. </view>

样式设置代码:

  1. /* pages/personal/personal.wxss */
  2. /* pages/personal/personal.wxss */
  3. .personalContainer {
  4. width: 100%;
  5. height: 100%;
  6. }
  7. .personalContainer .user-section {
  8. height: 520rpx;
  9. position: relative;
  10. padding: 100rpx 30rpx 0;
  11. }
  12. .user-section .bg {
  13. position: absolute;
  14. left: 0;
  15. top: 0;
  16. width: 100%;
  17. height: 100%;
  18. opacity: 0.7;
  19. filter: blur(1px);
  20. }
  21. .user-info-box{
  22. height: 180rpx;
  23. display:flex;
  24. align-items:center;
  25. position:relative;
  26. z-index: 1;
  27. }
  28. .user-info-box .portrait{
  29. width: 130rpx;
  30. height: 130rpx;
  31. border:5rpx solid #fff;
  32. border-radius: 50%;
  33. }
  34. .user-info-box .username{
  35. font-size: 24;
  36. color: #303133;
  37. margin-left: 20rpx;
  38. }
  39. /* vip-box */
  40. .vip-card-box {
  41. position: relative;
  42. display: flex;
  43. flex-direction: column;
  44. background: rgba(0, 0, 0, .7);
  45. height: 240rpx;
  46. color: #f7d680;
  47. border-radius: 16rpx 16rpx 0 0;
  48. padding: 20rpx 24rpx;
  49. }
  50. .vip-card-box .card-bg{
  51. position:absolute;
  52. top: 20rpx;
  53. right: 0;
  54. width: 380rpx;
  55. height: 260rpx;
  56. }
  57. .vip-card-box .b-btn{
  58. position: absolute;
  59. right: 20rpx;
  60. top: 16rpx;
  61. width: 132rpx;
  62. height: 40rpx;
  63. text-align: center;
  64. line-height: 40rpx;
  65. font-size: 22rpx;
  66. color: #36343c;
  67. border-radius: 20px;
  68. background: #f9e6af;
  69. z-index: 1;
  70. }
  71. .vip-card-box .b-btn{
  72. position: absolute;
  73. right: 20rpx;
  74. top: 16rpx;
  75. width: 132rpx;
  76. height: 40rpx;
  77. text-align: center;
  78. line-height: 40rpx;
  79. font-size: 22rpx;
  80. color: #36343c;
  81. border-radius: 20px;
  82. /*background: linear-gradient(left, #f9e6af, #ffd465);*/ /*渐变不生效*/
  83. background: #f9e6af;
  84. z-index: 1;
  85. }
  86. .vip-card-box .tit {
  87. font-size: 22rpx;
  88. color: #f7d680;
  89. margin-bottom: 28rpx;
  90. }
  91. .vip-card-box .tit .iconfont{
  92. color: #f6e5a3;
  93. margin-right: 16rpx;
  94. }
  95. .vip-card-box .e-m{
  96. font-size: 34rpx;
  97. margin-top: 10rpx;
  98. }
  99. .vip-card-box .e-b{
  100. font-size: 24rpx;
  101. color: #d8cba9;
  102. margin-top: 10rpx;
  103. }
  104. .cover-container{
  105. margin-top: -150rpx;
  106. padding: 0 30rpx;
  107. position:relative;
  108. background: #f5f5f5;
  109. padding-bottom: 20rpx;
  110. }
  111. .cover-container .arc{
  112. position:absolute;
  113. left: 0;
  114. top: -34rpx;
  115. width: 100%;
  116. height: 36rpx;
  117. }
  118. /* 导航部分 */
  119. .cover-container .nav-section {
  120. display: flex;
  121. background: #fff;
  122. padding: 20rpx 0;
  123. border-radius: 15rpx;
  124. }
  125. .nav-section .nav-item {
  126. width: 25%;
  127. box-sizing: border-box;
  128. display: flex;
  129. flex-direction: column;
  130. align-items: center;
  131. }
  132. .nav-section .nav-item .iconfont {
  133. font-size: 50rpx;
  134. color: #d43c33;
  135. line-height: 70rpx;
  136. }
  137. .nav-section .nav-item text:last-child {
  138. font-size: 22rpx;
  139. }
  140. /* 个人中心列表 */
  141. .personalContent {
  142. background: #fff;
  143. margin-top: 20rpx;
  144. }
  145. /* 最近播放 */
  146. .personalContent .scrollView {
  147. display: flex;
  148. height: 160rpx;
  149. }
  150. .personalContent .recentPlay {
  151. display: flex;
  152. }
  153. .recentPlayContainer .title {
  154. padding-left: 20rpx;
  155. font-size: 26rpx;
  156. color: #333;
  157. line-height: 80rpx;
  158. }
  159. .personalContent .recentPlay image {
  160. width: 160rpx;
  161. height: 160rpx;
  162. margin-left: 20rpx;
  163. border-radius: 20rpx;
  164. }
  165. .cardList {
  166. margin-top: 20rpx;
  167. }
  168. .cardList .card-item{
  169. border-top: 1rpx solid #eee;
  170. height: 80rpx;
  171. line-height: 80rpx;
  172. padding: 10rpx;
  173. font-size: 26rpx;
  174. color: #333;
  175. }
  176. .cardList .card-item .more {
  177. float: right;
  178. }
  179. /*最近播放记录*/
  180. .recentScroll {
  181. display: flex;
  182. height: 200rpx;
  183. }
  184. .recentItem {
  185. margin: 0 20rpx;
  186. }
  187. .recentItem image{
  188. width: 200rpx;
  189. height: 200rpx;
  190. border-radius: 10rpx;
  191. }
  192. .logout-btn {
  193. margin-top: 5rpx;
  194. }

功能实现:

  1. // pages/personal/personal.js
  2. import request from '../../utils/request'
  3. let startY = 0;//手指起始坐标
  4. let moveY = 0;//手指移动坐标
  5. let moveDistance = 0;//手指移动距离
  6. Page({
  7. /**
  8. * 页面的初始数据
  9. */
  10. data: {
  11. coverTransform: 'translateY(0)',
  12. coverTransition: '',
  13. userInfo: {},
  14. recentPlayList: [],//播放记录
  15. isLogin: false //控制退出登录显示
  16. },
  17. /**
  18. * 生命周期函数--监听页面加载
  19. */
  20. onLoad: function (options) {
  21. //读取用户基本信息
  22. let userInfo = wx.getStorageSync('userInfo');
  23. if(userInfo){
  24. //更新用户信息
  25. this.setData({
  26. userInfo: JSON.parse(userInfo),
  27. isLogin: true
  28. })
  29. //播放记录
  30. this.getUserRecentPlayList(this.data.userInfo.userId)
  31. }
  32. },
  33. //获取用户播放记录
  34. async getUserRecentPlayList(userId){
  35. let recentPlayListData = await request('/user/record',{uid: userId,type: 0});
  36. let index = 0;
  37. let recentPlayList = recentPlayListData.allData.splice(0,10).map(item=>{
  38. item.id = index++;
  39. return item;
  40. })
  41. this.setData({
  42. recentPlayList: recentPlayList
  43. })
  44. },
  45. handleTouchStart(event){
  46. this.setData({
  47. coverTransition: ''
  48. })
  49. //起始手指坐标
  50. startY = event.touches[0].clientY;
  51. },
  52. handleTouchMove(event){
  53. moveY = event.touches[0].clientY;
  54. moveDistance = moveY - startY;
  55. if(moveDistance <= 0){
  56. return;
  57. }
  58. if(moveDistance >= 80){
  59. moveDistance = 80;
  60. }
  61. //动态更新coverTransform
  62. this.setData({
  63. coverTransform: 'translateY('+moveDistance+'rpx)'
  64. })
  65. },
  66. handleTouchEnd(){
  67. //重置coverTransform
  68. this.setData({
  69. coverTransform: 'translateY(0)',
  70. coverTransition: 'transform 1s linear'
  71. })
  72. },
  73. //登录跳转
  74. toLogin(){
  75. wx.navigateTo({
  76. url: '/pages/login/login',
  77. })
  78. },
  79. //退出登录
  80. logout(){
  81. //服务端退出登录状态
  82. let status = request('/logout');
  83. console.log(status)
  84. //删除客户端信息
  85. wx.removeStorageSync('userInfo')
  86. this.setData({
  87. userInfo: {},
  88. isLogin: false
  89. })
  90. wx.navigateTo({
  91. url: '/pages/login/login',
  92. })
  93. wx.showToast({
  94. title: '退出登录',
  95. icon: 'success'
  96. })
  97. },
  98. /**
  99. * 生命周期函数--监听页面初次渲染完成
  100. */
  101. onReady: function () {
  102. },
  103. /**
  104. * 生命周期函数--监听页面显示
  105. */
  106. onShow: function () {
  107. },
  108. /**
  109. * 生命周期函数--监听页面隐藏
  110. */
  111. onHide: function () {
  112. },
  113. /**
  114. * 生命周期函数--监听页面卸载
  115. */
  116. onUnload: function () {
  117. },
  118. /**
  119. * 页面相关事件处理函数--监听用户下拉动作
  120. */
  121. onPullDownRefresh: function () {
  122. },
  123. /**
  124. * 页面上拉触底事件的处理函数
  125. */
  126. onReachBottom: function () {
  127. },
  128. /**
  129. * 用户点击右上角分享
  130. */
  131. onShareAppMessage: function () {
  132. }
  133. })

推荐歌曲区域:

目录:

推荐歌曲结构代码:

  1. <!--pages/recommendSong/recommendSong.wxml-->
  2. <view class="recommendSongContainer">
  3. <!-- 头部 -->
  4. <view class="header">
  5. <image src="/static/images/recommendSong/recommend.jpg"></image>
  6. <view class="date">
  7. <text class="year">{{year}} / </text>
  8. <text class="month">{{month}} / </text>
  9. <text class="day">{{day}}</text>
  10. </view>
  11. </view>
  12. <!-- 列表区域 -->
  13. <view class="ListContainer">
  14. <view class="listHeader">
  15. <text>播放全部</text>
  16. <text class="changeMore">多选</text>
  17. </view>
  18. <!-- 歌曲 -->
  19. <scroll-view scroll-y="true" class="listScroll">
  20. <view class="scrollItem" wx:for="{{recommendList}}" wx:key="id" data-index="{{index}}" data-song="{{item}}" bindtap="toSongDetail">
  21. <image src="{{item.album.picUrl}}"></image>
  22. <view class="musicInfo">
  23. <text class="musicName">{{item.name}}</text>
  24. <text class="musicAuthor">{{item.artists[0].name}}</text>
  25. </view>
  26. <text class="iconfont icon-icon"></text>
  27. </view>
  28. </scroll-view>
  29. </view>
  30. </view>

推荐歌曲样式代码:

  1. /* pages/recommendSong/recommendSong.wxss */
  2. /* 头部 */
  3. .recommendSongContainer .header {
  4. position: relative;
  5. width: 100%;
  6. height: 300rpx;
  7. }
  8. .recommendSongContainer .header image {
  9. width: 100%;
  10. height: 100%;
  11. }
  12. .recommendSongContainer .header .date {
  13. position: absolute;
  14. left: 30%;
  15. top:20%;
  16. width: 300rpx;
  17. height: 100rpx;
  18. text-align: center;
  19. line-height: 100rpx;
  20. color: #B9B3E1;
  21. font-size: 40rpx;
  22. }
  23. .date .month {
  24. font-size: 60rpx;
  25. }
  26. /* 列表 */
  27. .ListContainer {
  28. position: relative;
  29. top: -20rpx;
  30. padding: 0 20rpx;
  31. border-radius: 30rpx;
  32. background: #fff;
  33. }
  34. .listHeader {
  35. height: 80rpx;
  36. line-height: 80rpx;
  37. }
  38. .listHeader .changeMore {
  39. float: right;
  40. }
  41. /* 歌曲 */
  42. .listScroll {
  43. height: calc(100vh - 380rpx);/*整体高度减去除了scroll-view的元素的高度,即可得到只使得scroll-view元素滑动效果*/
  44. }
  45. .scrollItem {
  46. display: flex;
  47. position: relative;
  48. margin-bottom: 20rpx;
  49. }
  50. .scrollItem image {
  51. height: 80rpx;
  52. width: 80rpx;
  53. border-radius: 8rpx;
  54. }
  55. .musicInfo {
  56. display: flex;
  57. flex-direction: column;
  58. margin-left: 20rpx;
  59. }
  60. .musicInfo text {
  61. height: 40rpx;
  62. line-height: 40rpx;
  63. font-size: 26rpx;
  64. max-width: 400rpx;/*最长文本长度*/
  65. white-space: nowrap;/*不允许换行*/
  66. overflow: hidden;/*超过部分隐藏*/
  67. text-overflow: ellipsis;/*超过部分换为省略号*/
  68. }
  69. .scrollItem .iconfont{
  70. position: absolute;
  71. right: 10rpx;
  72. width: 80rpx;
  73. height: 80rpx;
  74. text-align: right;
  75. }

功能实现代码:

  1. // pages/recommendSong/recommendSong.js
  2. import PubSub from 'pubsub-js';
  3. import request from '../../utils/request'
  4. Page({
  5. /**
  6. * 页面的初始数据
  7. */
  8. data: {
  9. year: '',//年
  10. month: '',//月
  11. day: '',//天
  12. recommendList: [],//推荐列表数据
  13. index: 0,//音乐下标
  14. },
  15. /**
  16. * 生命周期函数--监听页面加载
  17. */
  18. onLoad: function (options) {
  19. //判断用户是否登录
  20. let userIinfo = wx.getStorageSync('userInfo');
  21. if(!userIinfo){
  22. wx.showToast({
  23. title: '请先进行登录',
  24. icon: 'none',
  25. success: ()=>{
  26. //跳转至登录界面
  27. wx.reLaunch({
  28. url: '/pages/login/login',
  29. })
  30. }
  31. })
  32. }
  33. let nowTime = new Date();
  34. //更新日期
  35. this.setData({
  36. day: nowTime.getDate(),
  37. month: nowTime.getMonth() + 1,
  38. year: nowTime.getFullYear()
  39. })
  40. //获取每日推荐的数据
  41. this.getRecommendList();
  42. //订阅来自songDetail页面发布的消息
  43. PubSub.subscribe('switchMusic',(msg,type) => {
  44. let {recommendList,index} = this.data;
  45. if(type === 'pre'){//上一首
  46. (index === 0) && (index = recommendList.length);
  47. index -= 1;
  48. }else{//下一首
  49. (index === recommendList.length - 1) && (index = -1);
  50. index += 1;
  51. }
  52. //更新下标
  53. this.setData({
  54. index: index
  55. })
  56. let musicId = recommendList[index].id;
  57. //将音乐id回传给songDetail页面
  58. PubSub.publish('musicId',musicId);
  59. })
  60. },
  61. //获取每日推荐数据
  62. async getRecommendList(){
  63. let recommendListData = await request('/recommend/songs');
  64. this.setData({
  65. recommendList: recommendListData.recommend
  66. })
  67. },
  68. //跳转至songDetail页面
  69. toSongDetail(event){
  70. let {song,index} = event.currentTarget.dataset;
  71. this.setData({
  72. index: index
  73. })
  74. //路由跳转传参:query参数
  75. wx.navigateTo({
  76. url: '/pages/songDetail/songDetail?song=' + song.id
  77. })
  78. },
  79. /**
  80. * 生命周期函数--监听页面初次渲染完成
  81. */
  82. onReady: function () {
  83. },
  84. /**
  85. * 生命周期函数--监听页面显示
  86. */
  87. onShow: function () {
  88. },
  89. /**
  90. * 生命周期函数--监听页面隐藏
  91. */
  92. onHide: function () {
  93. },
  94. /**
  95. * 生命周期函数--监听页面卸载
  96. */
  97. onUnload: function () {
  98. },
  99. /**
  100. * 页面相关事件处理函数--监听用户下拉动作
  101. */
  102. onPullDownRefresh: function () {
  103. },
  104. /**
  105. * 页面上拉触底事件的处理函数
  106. */
  107. onReachBottom: function () {
  108. },
  109. /**
  110. * 用户点击右上角分享
  111. */
  112. onShareAppMessage: function () {
  113. }
  114. })

播放歌曲页面:

目录:

结构代码:

  1. <!--pages/songDetail/songDetail.wxml-->
  2. <view class="songDetailContainer">
  3. <view class="musicAuthor">{{song.ar[0].name}}</view>
  4. <view class="circle"></view>
  5. <!-- 摇杆 -->
  6. <image class="needle {{isPlay && 'needleRotate'}}" src="/static/images/song/needle.png"></image>
  7. <!-- 磁盘 -->
  8. <view class="discContainer {{isPlay && 'discAnimation'}}">
  9. <image class="disc" src="/static/images/song/disc.png"></image>
  10. <!-- 歌曲封面图 -->
  11. <image class="musicImg" src="{{song.al.picUrl}}"></image>
  12. </view>
  13. <!-- 歌词 -->
  14. <view class="scrollLrc">
  15. <text>{{currentLyric}}</text>
  16. </view>
  17. <!-- 进度条控制 -->
  18. <view class="progressControl">
  19. <text>{{currentTime}}</text>
  20. <!-- 总进度条 -->
  21. <view class="barControl">
  22. <!-- 实时进度条 -->
  23. <view class="audio-currentTime-Bar" style="width: {{currentWidth + 'rpx'}}">
  24. <!-- 小圆球 -->
  25. <view class="audio-circle"></view>
  26. </view>
  27. </view>
  28. <text>{{durationTime}}</text>
  29. </view>
  30. <!-- 歌曲播放控制 -->
  31. <view class="musicControl">
  32. <text class="iconfont icon-random"></text>
  33. <text class="iconfont icon-diyigeshipin" id="pre" bindtap="handleSwitch"></text>
  34. <text class="iconfont {{isPlay ? 'icon-zanting' : 'icon-kaishi'}} big" bindtap="handleMusicPlay"></text>
  35. <text class="iconfont icon-zuihouyigeshipin" id="next" bindtap="handleSwitch"></text>
  36. <text class="iconfont icon-liebiao"></text>
  37. </view>
  38. </view>

样式代码:

  1. /* pages/songDetail/songDetail.wxss */
  2. .songDetailContainer {
  3. height: 100%;
  4. background: rgba(0,0,0,0.5);
  5. display: flex;
  6. flex-flow: column;
  7. align-items: center;
  8. }
  9. /* 底座 */
  10. .circle {
  11. position: relative;
  12. z-index: 100;
  13. width: 60rpx;
  14. height: 60rpx;
  15. border-radius: 50%;
  16. background: #fff;
  17. margin: 10rpx 0;
  18. }
  19. /* 摇杆 */
  20. .needle {
  21. position: relative;
  22. z-index: 99;
  23. top: -40rpx;
  24. left: 56rpx;
  25. width: 192rpx;
  26. height: 274rpx;
  27. transform-origin: 40rpx 0;
  28. transform: rotate(-20deg);
  29. transition: transform 1s;
  30. }
  31. /* 摇杆落下 */
  32. .needleRotate {
  33. transform: rotate(0deg);
  34. }
  35. .discContainer {
  36. position: relative;
  37. top: -170rpx;
  38. width: 598rpx;
  39. height: 598rpx;
  40. }
  41. .discAnimation {
  42. animation: disc 20s linear infinite;
  43. animation-delay: 1s;
  44. }
  45. /*设置动画帧 1.from to(只有起始帧和结束帧) 2.百分比(不止两帧)*/
  46. @keyframes disc{
  47. from {
  48. transform: rotate(0deg);
  49. }
  50. to {
  51. transform: rotate(360deg);
  52. }
  53. }
  54. /* 磁盘 */
  55. .disc {
  56. width: 100%;
  57. height: 100%;
  58. }
  59. /* 歌曲封面 */
  60. .musicImg {
  61. position: absolute;
  62. top: 0;
  63. left: 0;
  64. bottom: 0;
  65. right: 0;
  66. margin: auto;
  67. width: 370rpx;
  68. height: 370rpx;
  69. border-radius: 50%;
  70. }
  71. /* 歌词显示 */
  72. .scrollLrc {
  73. position: absolute;
  74. bottom: 280rpx;
  75. width: 640rpx;
  76. height: 120rpx;
  77. line-height: 120rpx;
  78. text-align: center;
  79. }
  80. /* 底部控制器 */
  81. .musicControl {
  82. position: absolute;
  83. bottom: 40rpx;
  84. left: 0;
  85. border-top: 1rpx solid #fff;
  86. width: 100%;
  87. display: flex;
  88. }
  89. .musicControl text {
  90. width: 20%;
  91. height: 120rpx;
  92. line-height: 120rpx;
  93. text-align: center;
  94. color: #fff;
  95. font-size: 50rpx;
  96. }
  97. .musicControl text.big {
  98. font-size: 80rpx;
  99. }
  100. /* 进度条控制 */
  101. .progressControl {
  102. position: absolute;
  103. bottom: 200rpx;
  104. width: 640rpx;
  105. height: 80rpx;
  106. line-height: 80rpx;
  107. display: flex;
  108. }
  109. .barControl {
  110. position: relative;
  111. width: 450rpx;
  112. height: 4rpx;
  113. background: rgba(0,0,0,0.4);
  114. margin: auto;
  115. }
  116. .audio-currentTime-Bar {
  117. position: absolute;
  118. top: 0;
  119. left: 0;
  120. z-index: 1;
  121. height: 4rpx;
  122. background: red;
  123. }
  124. /* 小圆球 */
  125. .audio-circle {
  126. position: absolute;
  127. right: -12rpx;
  128. top: -4rpx;
  129. width: 12rpx;
  130. height: 12rpx;
  131. border-radius: 50%;
  132. background: #fff;
  133. }

功能代码:

  1. // pages/songDetail/songDetail.js
  2. import PubSub from 'pubsub-js';
  3. import moment from 'moment';
  4. import request from '../../utils/request';
  5. //获取全局实例
  6. const appInstance = getApp();
  7. Page({
  8. /**
  9. * 页面的初始数据
  10. */
  11. data: {
  12. isPlay: false,//标识播放状态
  13. song: {},//歌曲详情对象
  14. musicId: '',//歌曲Id
  15. currentTime: '00:00',//当前时长
  16. durationTime:'00:00',//总时长
  17. currentWidth: 0,//实时进度条宽度
  18. lyric: [],//歌词
  19. lyricTime: 0,//歌词对应的时间
  20. currentLyric: "",//当前歌词对象
  21. },
  22. /**
  23. * 生命周期函数--监听页面加载
  24. */
  25. onLoad: function (options) {
  26. //options路由跳转参数
  27. let musicId = options.song;
  28. this.setData({
  29. musicId: musicId
  30. })
  31. this.getMusicInfo(musicId);
  32. this.getLyric(musicId);
  33. //判断当前页面音乐是否在播放
  34. if(appInstance.globalData.isMusicPlay && appInstance.globalData.musicId === musicId){
  35. //修改当前页面音乐播放状态
  36. this.setData({
  37. isPlay: true
  38. })
  39. }
  40. //创建控制音乐播放实例对象
  41. this.backgroundAudioManager = wx.getBackgroundAudioManager();
  42. //监视音乐播放与暂停
  43. this.backgroundAudioManager.onPlay(()=>{
  44. //修改音乐播放状态
  45. this.changePlayState(true);
  46. appInstance.globalData.musicId = musicId;
  47. });
  48. this.backgroundAudioManager.onPause(()=>{
  49. this.changePlayState(false);
  50. });
  51. this.backgroundAudioManager.onStop(()=>{
  52. this.changePlayState(false);
  53. });
  54. //音乐播放自然结束
  55. this.backgroundAudioManager.onEnded(()=>{
  56. //切歌
  57. PubSub.publish('switchMusic','next');
  58. this.setData({
  59. currentWidth: 0,
  60. currentTime: '00:00',
  61. lyric: [],
  62. lyricTime: 0,
  63. })
  64. })
  65. //监听音乐实时播放的进度
  66. this.backgroundAudioManager.onTimeUpdate(() => {
  67. //获取歌词对应时间
  68. let lyricTime = Math.ceil(this.backgroundAudioManager.currentTime);
  69. let currentTime = moment(this.backgroundAudioManager.currentTime * 1000).format('mm:ss');
  70. let currentWidth = (this.backgroundAudioManager.currentTime/this.backgroundAudioManager.duration) * 450;
  71. this.setData({
  72. lyricTime,
  73. currentTime,
  74. currentWidth
  75. })
  76. this.getCurrentLyric();
  77. })
  78. },
  79. //修改播放状态
  80. changePlayState(isPlay){
  81. this.setData({
  82. isPlay: isPlay
  83. })
  84. //修改全局播放状态
  85. appInstance.globalData.isMusicPlay = isPlay;
  86. },
  87. //点击暂停/播放的回调
  88. handleMusicPlay(){
  89. //修改是否播放的状态
  90. let isPlay = !this.data.isPlay;
  91. // this.setData({
  92. // isPlay: isPlay
  93. // })
  94. let {musicId} = this.data;
  95. this.musicControl(isPlay,musicId);
  96. },
  97. //请求歌曲信息
  98. async getMusicInfo(musicId){
  99. let songData = await request('/song/detail',{ids: musicId});
  100. let durationTime = moment(songData.songs[0].dt).format('mm:ss');
  101. this.setData({
  102. song: songData.songs[0],
  103. durationTime: durationTime
  104. })
  105. //动态修改窗口标题
  106. wx.setNavigationBarTitle({
  107. title: this.data.song.name
  108. })
  109. },
  110. //歌曲播放控制功能
  111. async musicControl(isPlay,musicId){
  112. if(isPlay){//音乐播放
  113. //获取音频资源
  114. let musicLinkData = await request('/song/url',{id: musicId})
  115. let musicLink = musicLinkData.data[0].url;
  116. console.log(musicLink)
  117. if(musicLink === null){
  118. wx.showToast({
  119. title: '请开通会员后听取',
  120. icon: 'none'
  121. })
  122. return;
  123. }
  124. //歌曲播放
  125. this.backgroundAudioManager.src = musicLink;
  126. this.backgroundAudioManager.title = this.data.song.name;
  127. }else{//音乐暂停
  128. this.backgroundAudioManager.pause();
  129. }
  130. },
  131. //歌曲切换
  132. handleSwitch(event){
  133. //切换类型
  134. let type = event.currentTarget.id;
  135. //关闭当前播放音乐
  136. this.backgroundAudioManager.stop();
  137. //订阅来自recommendSong页面
  138. PubSub.subscribe('musicId',(msg,musicId) => {
  139. //获取歌曲
  140. this.getMusicInfo(musicId);
  141. //自动播放当前音乐
  142. this.musicControl(true,musicId);
  143. //取消订阅
  144. PubSub.unsubscribe('musicId');
  145. })
  146. //发布消息数据给recommendSong页面
  147. PubSub.publish('switchMusic',type);
  148. },
  149. //获取歌词
  150. async getLyric(musicId){
  151. let lyricData = await request("/lyric", {id: musicId});
  152. let lyric = this.formatLyric(lyricData.lrc.lyric);
  153. },
  154. //传入初始歌词文本text
  155. formatLyric(text) {
  156. let result = [];
  157. let arr = text.split("\n"); //原歌词文本已经换好行了方便很多,我们直接通过换行符“\n”进行切割
  158. let row = arr.length; //获取歌词行数
  159. for (let i = 0; i < row; i++) {
  160. let temp_row = arr[i]; //现在每一行格式大概就是这样"[00:04.302][02:10.00]hello world";
  161. let temp_arr = temp_row.split("]");//我们可以通过“]”对时间和文本进行分离
  162. let text = temp_arr.pop(); //把歌词文本从数组中剔除出来,获取到歌词文本了!
  163. //再对剩下的歌词时间进行处理
  164. temp_arr.forEach(element => {
  165. let obj = {};
  166. let time_arr = element.substr(1, element.length - 1).split(":");//先把多余的“[”去掉,再分离出分、秒
  167. let s = parseInt(time_arr[0]) * 60 + Math.ceil(time_arr[1]); //把时间转换成与currentTime相同的类型,方便待会实现滚动效果
  168. obj.time = s;
  169. obj.text = text;
  170. result.push(obj); //每一行歌词对象存到组件的lyric歌词属性里
  171. });
  172. }
  173. result.sort(this.sortRule) //由于不同时间的相同歌词我们给排到一起了,所以这里要以时间顺序重新排列一下
  174. this.setData({
  175. lyric: result
  176. })
  177. },
  178. sortRule(a, b) { //设置一下排序规则
  179. return a.time - b.time;
  180. },
  181. //控制歌词播放
  182. getCurrentLyric(){
  183. let j;
  184. for(j=0; j<this.data.lyric.length-1; j++){
  185. if(this.data.lyricTime == this.data.lyric[j].time){
  186. this.setData({
  187. currentLyric : this.data.lyric[j].text
  188. })
  189. }
  190. }
  191. },
  192. /**
  193. * 生命周期函数--监听页面初次渲染完成
  194. */
  195. onReady: function () {
  196. },
  197. /**
  198. * 生命周期函数--监听页面显示
  199. */
  200. onShow: function () {
  201. },
  202. /**
  203. * 生命周期函数--监听页面隐藏
  204. */
  205. onHide: function () {
  206. },
  207. /**
  208. * 生命周期函数--监听页面卸载
  209. */
  210. onUnload: function () {
  211. },
  212. /**
  213. * 页面相关事件处理函数--监听用户下拉动作
  214. */
  215. onPullDownRefresh: function () {
  216. },
  217. /**
  218. * 页面上拉触底事件的处理函数
  219. */
  220. onReachBottom: function () {
  221. },
  222. /**
  223. * 用户点击右上角分享
  224. */
  225. onShareAppMessage: function () {
  226. }
  227. })

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/77869
推荐阅读
相关标签
  

闽ICP备14008679号