仿网易云音乐微信小程序

栏目: JavaScript · 发布时间: 6年前

内容简介:前一阵子学习了微信小程序,为了巩固所学的知识和提高实战经验,决定自己手撸一款小程序。因为听歌一直在用网易云音乐,所以突发奇想就做一款仿网易云音乐的小程序吧!开发中遇到了很多在学习中没有遇到过的坑,也很感谢在我改不出BUG时给予帮助的老师学长同学!本着学习和分享的目的,写下以下的文字,希望能给#开发前准备一般我们https请求都是通过
仿网易云音乐微信小程序
仿网易云音乐微信小程序
仿网易云音乐微信小程序

前言

前一阵子学习了微信小程序,为了巩固所学的知识和提高实战经验,决定自己手撸一款小程序。因为听歌一直在用网易云音乐,所以突发奇想就做一款仿网易云音乐的小程序吧!开发中遇到了很多在学习中没有遇到过的坑,也很感谢在我改不出BUG时给予帮助的老师学长同学!本着学习和分享的目的,写下以下的文字,希望能给 初学小程序的你 带来一点帮助,大佬轻点喷。

#开发前准备

  • VScode代码编辑器。
  • 微信开发者工具
  • ios网易云音乐(V5.9.1版本)
  • 酷狗音乐小程序(提供了一些思路)
  • 网易云音乐API
  • (阿里巴巴矢量图标库)提供一些图标icon

tabBar部分

自定义tabBar

一般在开发中,微信小程序给我们的tabBar就能满足需求。但是,有些特别的需求必须使用自定义tabBar才能满足。 比如tabBar实现半透明。那么,如何才能自定义tabBar呢?

1.首先,在 app.json里的"tabBar"里声明 "tabBar": { "custom": true }
2.接着在项目的根目录下新建一个 custom-tab-bar 文件夹。里面包含 index.wxml index.js index.json index.wxss

四个文件。更多细节参考微信小程序文档

developers.weixin.qq.com/miniprogram…

仿网易云音乐微信小程序
<!-- index.html -->

<!-- 自定义tabbar页面 -->
<cover-view class="tab-bar">
   <cover-view class="tab-bar-border"></cover-view><!--tabBal边框样式  -->
<!-- 乐库tabbar -->
  <cover-view class='tab-bar-item' >
    <cover-image src='../images/music.png' hidden='{{isShow_index}}' bindtap='switchTab_index'></cover-image>
    <cover-image src='../images/selected-music.png' hidden='{{!isShow_index}}' bindtap='switchTab_index'></cover-image>
    <cover-view style="color:{{isShow_index ? selectedColor : color}}">乐库</cover-view>
  </cover-view>

<!-- 播放tabbar -->
    <cover-view class='tab-bar-item' bindtap='switchTab_playing'>
    <cover-image src='../images/selected-playing.png' hidden='{{isShow_playing}}'></cover-image>
    <cover-image src='../images/playing.png' hidden='{{!isShow_playing}}'></cover-image>
    <cover-view></cover-view>
  </cover-view>

<!-- 我的tabbar -->
    <cover-view class='tab-bar-item' bindtap='switchTab_me'>
    <cover-image src='../images/me.png' hidden='{{isShow_me}}'></cover-image>
    <cover-image src='../images/selected-me.png' hidden='{{!isShow_me}}'></cover-image>
    <cover-view style="color:{{isShow_me ? selectedColor : color}}">我的</cover-view>
  </cover-view>
</cover-view>
复制代码
// index.js
Component({
  data: {
    isShow_index:true,
    isShow_playing:false,
    isShow_me:false,
    selected: 0, //首页
    color: "#8D8D8D",
    selectedColor: "#C62F2F",
    list: [{
      pagePath: "/pages/index/index",
      iconPath: "/images/music.png",
      selectedIconPath: "/images/selected-music.png",
      text: "乐库"
    }, {
      pagePath: "/pages/love/love",
        iconPath: "/images/selected-playing.png",
      selectedIconPath: "/images/playing.png",
      text: ""
    },
      {
        pagePath: "/pages/me/me",
        iconPath: "/images/me.png",
        selectedIconPath: "/images/selected-me.png",
        text: "我的"
      }]
  },

  methods: {
    switchTab_index:function(){
      wx.switchTab({
        url:'/pages/index/index'
      })
      this.setData({
        isShow_index: true,
        isShow_me: false,
        isShow_playing: false
      })
    },
    switchTab_playing: function () {
      wx.switchTab({
        url: '/pages/love/love'
      })
      this.setData({
        isShow_playing: true,
        isShow_index: false,
        isShow_me: false
      })
    },
    switchTab_me: function () {
      wx.switchTab({
        url: '/pages/me/me'
      })
      this.setData({
        isShow_me:true,
        isShow_playing: false,
        isShow_index: false
      })
    }
  }
})
复制代码

tabBar半透明

仿网易云音乐微信小程序
/* custom-tab-bar/index.wxss */
.tab-bar {
  height:7%;
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  height: 48px;
  background:#FAFBFD;
  opacity: 0.93;
  display: flex;
  padding-bottom: env(safe-area-inset-bottom);
}
复制代码

API封装

一般我们https请求都是通过 wx.request 来请求,但是这种方法只能请求一次数据,如果首页用 wx.request 来请求的话,代码看起来会很冗长和杂乱。不仅自己容易搞糊涂,其他人看代码时也会很累。因此为了代码的整洁干净,我在这里新建了一个文件专门存放API。一般在根目录下的 utils 文件夹下新建一个 api.js ,但我在根目录下新建了文件夹 API ,里面包含 api.js

// api.js
const API_BASE_URL = 'http://musicapi.leanapp.cn';
const request = (url, data) => { 
  let _url = API_BASE_URL  + url;
  return new Promise((resolve, reject) => {
    wx.request({
      url: _url,
      method: "get",
      data: data,
      header: {
        'Content-Type': 'application/x-www-form-urlencoded'
      },
      success(request) {
        resolve(request.data)
        
      },
      fail(error) {
        reject(error)
      }
    })
  });
}


module.exports ={
  gethotsongs:(data) =>{
    return request('/search/hot',data)//热搜接口
  },
  searchSuggest:(data)=>{
    return request('/search/suggest',data)//搜索建议接口
  },
  searchResult:(data)=>{
    return request('/search',data)//搜索结果接口
  },
  getBanner:(data)=>{
    return request('/banner',data)//个性推荐轮播
  },
  getsongsheet:(data)=>{
    return request('/top/playlist',data)//热门歌单接口
  },
  getNewSong:(data)=>{
    return request('/personalized/newsong',data)//最新音乐接口
  },
  getDjRadios:(data)=>{
    return request('/dj/recommend',data)//电台推荐接口
  },
  getProgramRecommend:(data)=>{
    return request('/program/recommend',data)//推荐节目接口
  },
  getRecommendType:(data)=>{
    return request('/dj/recommend/type',data)//所有电台分类推荐
  },
  getRecommendMV:(data)=>{
    return request('/personalized/mv',data)//推荐MV
  },
  getNewMv:(data)=>{
    return request('/mv/first',data)//最新MV
  },
  getNewEst:(data)=>{
    return request('/album/newest',data)//最新专辑
  },
  getTopList:(data)=>{
    return request('/top/list',data)//排行榜
  },
  getDjList:(data)=>{
    return request('/dj/catelist',data) //电台分类
  },
  getPay:(data)=>{
    return request('/dj/paygift',data)//付费精品
  },
  getSonger:(data)=>{
    return request('/toplist/artist',data)//歌手排行
  }
}
复制代码

api.js 只能通过 module.exports 来暴露,那个页面要数据就从这拿。如果在哪个页面要用到它,还需要在头部引入一下:

const API = require('../../API/api')

以个性推荐轮播图为例,

getBanner: function() {
    API.getBanner({
      type: 2
    }).then(res => {
      if (res.code === 200) { //更加严谨
        this.setData({
          banner: res.banners
        })
      }
    })
  }
复制代码

这样就把请求到的数据存储到 banner 中了。

搜索部分

输入框样式

仿网易云音乐微信小程序
我这里是引入了 WEUI 的样式, 1.下载 weui.wxss ,链接我找不到了,所以我放上了我的 github 上的 weui.wxss 。 2.把下载好的 weui.wxss 放到根目录下。 3.在 app.wxss@import "weui.wxss"; 引入一下就可以使用微信提供给我们的样式了。 4. WeUI 样式库

热门搜索

仿网易云音乐微信小程序
上面已经提到我从 api.js

中拿数据。

// 从接口到获取到数据导入到hotsongs
  gethotsongs() {
    API.gethotsongs({ type: 'new' }).then(res => {
      wx.hideLoading()
      if (res.code === 200) {  //严谨
        this.setData({
          hotsongs: res.result.hots
        })
      }
    })
  }
复制代码

搜索历史

仿网易云音乐微信小程序
思路:当在输入框输入完成后-->失去焦点--> 利用 wx.setStorageSync 存进缓存中--> wx.getStorageSync

获取到并把它打印出来。

// input失去焦点函数
  routeSearchResPage: function(e) {
    console.log(e.detail.value)
    let history = wx.getStorageSync("history") || [];
    history.push(this.data.searchKey)
    wx.setStorageSync("history", history);
  },

//每次显示变动就去获取缓存,给history,并for出来。
  onShow: function () {
    this.setData({
      history: wx.getStorageSync("history") || []
    })
  },
复制代码

清空搜索历史

仿网易云音乐微信小程序
思路:×图标绑定事件->呼出对话框 wx.showModal ->确定则把 history

赋值为空

// 清空page对象data的history数组 重置缓存为[]
clearHistory: function() {
  const that = this;
  wx.showModal({
    content: '确认清空全部历史记录',
    cancelColor:'#DE655C',
    confirmColor: '#DE655C',
    success(res) {
      if (res.confirm) {
        that.setData({
          history: []
        })
        wx.setStorageSync("history", []) //把空数组给history,即清空历史记录
      } else if (res.cancel) {
      }
    }
  })
},
复制代码

实时搜索建议

仿网易云音乐微信小程序

思路:实时获取输入框的值->把值传给搜索建议API,发起网络请求->请求之后拿到搜索建议->打印结果并隐藏其他组件只保留搜索建议的组件(类似于Vue里的v-show)

//获取input文本并且实时搜索,动态隐藏组件
  getsearchKey:function(e){
    console.log(e.detail.value) //打印出输入框的值
    let that = this;
    if(e.detail.cursor != that.data.cursor){ //实时获取输入框的值
      that.setData({
        searchKey: e.detail.value
      })
    }
    if(e.value!=""){ //组件的显示与隐藏
      that.setData({
        showView: false
      })
    } else{
      that.setData({
        showView: ""
      })
    }
    if(e.detail.value!=""){ //解决 如果输入框的值为空时,传值给搜索建议,会报错的bug
      that.searchSuggest();
    }  
  }
复制代码
// 搜索建议
searchSuggest(){
  API.searchSuggest({ keywords: this.data.searchKey ,type:'mobile'}).then(res=>{
    if(res.code === 200){
      this.setData({
        searchsuggest:res.result.allMatch
      })
    }
  })
}
复制代码

点击热搜或历史,执行搜索

仿网易云音乐微信小程序
思路:关键是 event ,点击通过 e.currentTarget.dataset.value

拿到所点击的值,再交给其他方法执行搜索行为。

// 点击热门搜索值或搜索历史,填入搜索框
  fill_value:function(e){
    let that = this;
    console.log(history)
    // console.log(e.currentTarget.dataset.value)
    that.setData({
      searchKey: e.currentTarget.dataset.value,//点击吧=把值给searchKey,让他去搜索
      inputValue: e.currentTarget.dataset.value,//在输入框显示内容
      showView:false,//给false值,隐藏 热搜和历史 界面
      showsongresult: false, //给false值,隐藏搜索建议页面
    })
    that.searchResult(); //执行搜索功能
  }
复制代码

搜索结果

仿网易云音乐微信小程序
思路:输入结束->确认键->调用 searchResult

请求到结果

// 搜索完成点击确认
  searchover:function(){
    let that = this;
    that.setData({
      showsongresult: false
    })
    that.searchResult();
  }
复制代码
// 搜索结果
searchResult(){
  console.log(this.data.searchKey)
  API.searchResult({ keywords: this.data.searchKey, type: 1, limit: 100, offset:2 }).then(res => {
    if (res.code === 200) {
      this.setData({
        searchresult: res.result.songs
      })
    }
  })
}
复制代码

乐库部分

仿网易云音乐微信小程序
乐库部分其实没什么逻辑很难的部分,以结构和样式为主,在这里就不赘述了。可以到我的 github

上查看。在这里分享一些小功能的实现和踩到的坑。

个性推荐,主播电台切换

仿网易云音乐微信小程序

1.个性推荐和主播电台是两个 swiper-item 所以他们才可以左右滑动,就像轮播图一样,不过轮播图放的是图片,而这里放的是整个页面。 2.我要实现的效果是左右滑动的同时, 个性推荐主播电台 下面的白色方块也要跟着滑动。

1. 第一种方法

给包裹两个 swiper-itemswiper 添加一个 bindchange="changeline" 事件,把事件对象 event 打印出来发现, console.log(e.detail.current) ,当我们左右滑动的时候 cuurrent 的值会在 01

之间切换。所以我给白色方块添加

class="{{changeline?'swiper_header_line_before':'swiper_header_line_after'}}"
复制代码
if(e.detail.current === 0){
    this.setData({
       changeline:true
      })
    }else{
    this.setData({
       changeline:false
      })
    }
复制代码

current 为0,即页面在个性推荐时,让 changelinetrue ;当 current 为1,即页面在主播电台时,让 changelinefalse ;为 true 时,给白色方块加持 swiper_header_line_before 的样式,为 false 时,加持 swiper_header_line_after 的样式。这样就可以跟随 swiper-item 的滑动而切换了。 但是,这种切换方式太僵硬了 ,没有那种流畅的切换效果,而且不适合多 swiper-item 页面。

2. 第二种方法

仿网易云音乐微信小程序

让一半宽度,四分之一宽度设置为变量是为了兼容不同的手机型号。因为写死数据肯定会有BUG,所以才要计算宽度。

<view class="weui-navbar-slider" style="transform:translateX({{slideOffset}}px);"></view>
复制代码
.weui-navbar-slider{
  width:28px;
  height: 5px;
  background: #ffffff;
  border-radius:10rpx;
  transition: transform .6s;
 }
复制代码

slideOffset 为变量,动态接受从 data 传来的数据。

onLoad:function(){
    wx.getSystemInfo({
      success: function (res) {
        // console.log(res.windowWidth)
        // console.log(res.windowWidth / 2 / 2)
        half = res.windowWidth / 2 ;
        quarter = res.windowWidth / 2 / 2;
        that.setData({
          slideOffset: quarter - 14 //onLoad的时候让 quarter - 14 给slideOffset,即一开始就让他在个性推荐的下面,否则onLoad的时候一开始在0的位置
        })
      }
    })
}

  changeline:function(e){
    // console.log(e)
    // console.log(e.detail.current)
    let current = e.detail.current; //获取swiper的current值
    if(e.detail.current === 0){
      this.setData({
        slideOffset: quarter - 14
      })
    }
    if(e.detail.current === 1){
      this.setData({
        slideOffset: (quarter - 14) + half
      })
    }
    if(e.detail.current === null){
      this.setData({
        slideOffset: quarter - 14
      })
    }
  }
复制代码

MV播放

仿网易云音乐微信小程序

主要是结构和样式,我直接上代码了。

<!-- play_mv.wxml -->
<view class="mv_box">
    <video src="{{mv.brs['480']}}" class="mv" autoplay="{{autoplay}}" loop="{{loop}}" direction="{{0}}" show-fullscreen-btn="{{showfullscreenbtn}}"
    show-center-play-btn="{{showcenterplaybtn}}" enable-progress-gesture="{{enableprogressgesture}}" show-mute-btn="{{showmutebtn}}" title="{{mv.name}}"
    play-btn-position="{{center}}" object-fit="{{objectfit}}"></video>
</view>

<view class="mv_name">{{mv.name}}</view>
<view class="mv_time"> 发行:  {{mv.publishTime}}</view>
<view class="mv_time mv_times">播放次数:  {{mv.playCount}}</view>
<view class="mv_time mv_desc">{{mv.desc}}</view>
<view class="mv_time mv_desc mv_other">点赞: {{mv.likeCount}}</view>
<view class="mv_time mv_desc mv_other">收藏: {{mv.subCount}}</view>
<view class="mv_time mv_desc mv_other">评论: {{mv.commentCount}}</view>
<view class="mv_time mv_desc mv_other">分享: {{mv.shareCount}}</view>
复制代码
/* play/play_mv.wxss */
.mv_box{
    width: 100%;
    height: 480rpx;
    margin-top:-2rpx;
}
.mv{
    width: 100%;
    height: 100%;
    border-radius:15rpx;
}
.mv_name{
    margin-top:20rpx;
    margin-left:20rpx;
}
.mv_time{
    font-size: 12px;
    margin-left:20rpx;
    color:#979798;
    display:initial;
}
.mv_times{
    margin-left: 100rpx;
}
.mv_desc{
    display: block;
    color:#6A6B6C;
}
.mv_other{
    display: block;
}
复制代码
// play_mv.js
const API_BASE_URL = 'http://musicapi.leanapp.cn';
const app = getApp();
Page({
  data: {
    mv: [],
    autoplay: true,
    loop: true,
    showfullscreenbtn: true,
    showcenterplaybtn: true,
    enableprogressgesture: true,
    showmutebtn: true,
    objectfit: 'contain',
  },
  onLoad: function (options) {
    // console.log(mv_url);
    const mvid = options.id; // onLoad()后获取到歌曲视频之类的id

    // 请求MV的地址,失败则播放出错,成功则传值给createBgAudio(后台播放管理器,让其后台播放)
    wx.request({
      url: API_BASE_URL + '/mv/detail',
      data: {
        mvid: mvid    
      },
      success: res => {
        console.log(res.data.data.brs['480'])
        console.log('歌曲音频url:', res)
        if (res.data.data.brs === null) {  //如果是MV 电台 广告 之类的就提示播放出错,并返回首页
          console.log('播放出错')
          wx.showModal({
            content: '服务器开了点小差~~',
            cancelColor: '#DE655C',
            confirmColor: '#DE655C',
            showCancel: false,
            confirmText: '返回',
            complete() {
              wx.switchTab({
                url: '/pages/index/index'
              })
            }
          })
        } else {
          this.setData({
            mv: res.data.data
          })
        }
      }
    })
  },
})
复制代码

歌手榜

仿网易云音乐微信小程序
// 歌手榜的js
const API = require('../../API/api');
const app = getApp();
Page({

  data: {
    songers: [], //歌手榜
  },

  onLoad: function (options) {
    wx.showLoading({
      title: '加载中',
    });
    this.getSonger();
  },

  getSonger: function () {
    API.getSonger({}).then(res => {
      wx.hideLoading()
      this.setData({
        songers: res.list.artists.slice(0, 100)
      })
    })
  },
  handleSheet: function (event) { //event 对象,自带,点击事件后触发,event有type,target,timeStamp,currentTarget属性
    const sheetId = event.currentTarget.dataset.id; //获取到event里面的歌曲id赋值给audioId
    wx.navigateTo({                                 //获取到id带着完整url后跳转到play页面
      url: `./moremore_songer?id=${sheetId}`
    })
  },
})
复制代码
<!-- 歌手榜结构 -->
<view wx:for="{{songers}}" wx:key="" class='songer_box' data-id="{{item.id}}" bindtap='handleSheet'>
  <view class='songer_index_box'>
    <text class='songer_index'>{{index + 1}}</text>
  </view>
  <view class='songer_img_box'>
  <image src="{{item.picUrl}}" class='songer_img'></image>
  </view>
  <view class='songer_name_box'>
  <text class='songer_name'>{{item.name}}</text>
  <text class='songer_score'>{{item.score}}热度</text>
  </view>
</view>
复制代码
// 歌手下级路由歌曲列表
const API_BASE_URL = 'http://musicapi.leanapp.cn';
const app = getApp();
Page({
  data: {
    songList: []
  },
  onLoad: function (options) {
    wx.showLoading({
      title: '加载中',
    });
    const sheetId = options.id;
    wx.request({
      url: API_BASE_URL + '/artists',
      data: {
        id: sheetId    
      },
      success: res => {
        const waitForPlay = new Array;
        for (let i = 0; i <= res.data.hotSongs.length - 1; i++) { //循环打印出其id
          waitForPlay.push(res.data.hotSongs[i].id) //循环push ID 到waitForPlay数组
          app.globalData.waitForPlaying = waitForPlay  //让waitForPlay数组给全局数组
          // console.log(app.globalData.waitForPlaying)
        }
        wx.hideLoading()
        console.log(res.data.hotSongs)
        this.setData({
          songList: res.data.hotSongs
        })
      }
    })
  },
  handlePlayAudio: function (event) { //event 对象,自带,点击事件后触发,event有type,target,timeStamp,currentTarget属性
    const audioId = event.currentTarget.dataset.id; //获取到event里面的歌曲id赋值给audioId
    wx.navigateTo({                                 //获取到id带着完整url后跳转到play页面
      url: `../../play/play?id=${audioId}`
    })
  }
})
复制代码
<!-- more/more_songer/moremore_songer.wxml歌手下面的歌曲 -->
<view class='search_result_songs'>
  <view wx:for="{{songList}}" wx:key="" class='search_result_song_item songer_box' data-id="{{item.id}}" bindtap='handlePlayAudio'>
    <view class='songer_index_box'>
      <text class='songer_index'>{{index + 1}}</text>
    </view>
    <view class='songer_img_box'>
      <view class='search_result_song_song_name'>{{item.name}}</view>
      <view class='search_result_song_song_art-album'>{{item.ar[0].name}} - {{item.al.name}}</view>
    </view>
  </view>
</view>
复制代码

推荐歌单

仿网易云音乐微信小程序
因为样式与排行榜类似,所以只放出图片,源码可以到我的 github

上查看。

榜单排行

仿网易云音乐微信小程序
请查看源码

换一换功能

仿网易云音乐微信小程序

思路:绑定点击事件->选取随机的三个数->给空值->push三个随机数进数组中->重新赋值。

// 换一换
  change_1:function(){
    let maxNum = this.data.more_recommend_create.length  //计算数据长度
    let r1 = parseInt(Math.random() * (maxNum - 0) + 0); //取【0-数据长度】内的整数随机数
    let r2 = parseInt(Math.random() * (maxNum - 0) + 0);
    let r3 = parseInt(Math.random() * (maxNum - 0) + 0);
    this.setData({
      recommend_create: []
    })
    //重新取3组数据
    this.data.recommend_create.push(this.data.more_recommend_create[r1])
    this.data.recommend_create.push(this.data.more_recommend_create[r2])
    this.data.recommend_create.push(this.data.more_recommend_create[r3])
    //重新赋值
    this.setData({
      recommend_create: this.data.recommend_create
    })
  }
复制代码

播放界面

仿网易云音乐微信小程序

图片太大,因此加快了播放。

播放功能

思路:利用 data-id="{{item.id}}" 获取到歌曲ID放在 event 中-> 通过 event 对象事件获取ID并跳转到播放页面 -> wx.request 获取到歌曲的音频地址及detail->背景音频管理器 wx.getBackgroundAudioManager() ->播放

以歌手榜下级路由歌曲列表为例,

<view wx:for="{{songList}}" wx:key="" class='search_result_song_item songer_box' data-id="{{item.id}}" bindtap='handlePlayAudio'>
复制代码
handlePlayAudio: function (event) { //event 对象,自带,点击事件后触发,event有type,target,timeStamp,currentTarget属性
    const audioId = event.currentTarget.dataset.id; //获取到event里面的歌曲id赋值给audioId
    wx.navigateTo({                                 //获取到id带着完整url后跳转到play页面
      url: `../../play/play?id=${audioId}`
    })
  }
复制代码
// play.js
const API_BASE_URL = 'http://musicapi.leanapp.cn';
const app = getApp();
Page({
data: {
  isPlay: '',
  song:[],
  innerAudioContext: {},
  show:true,
  showLyric:true,
  songid:[],
  history_songId:[]
},
onLoad: function (options) {
  const audioid = options.id; // onLoad()后获取到歌曲视频之类的id
  this.play(audioid); //把从wxml获取到的值传给play()
},
play: function (audioid){
  const audioId = audioid;
  app.globalData.songId = audioId;  //让每一个要播放的歌曲ID给全局变量的songId
  const innerAudioContext = wx.createInnerAudioContext();
  this.setData({
    innerAudioContext,
    isPlay: true
  })
  // 请求歌曲音频的地址,失败则播放出错,成功则传值给createBgAudio(后台播放管理器,让其后台播放)
  wx.request({
    url: API_BASE_URL + '/song/url',
    data: {
      id: audioId
    },
    success: res => {
      if (res.data.data[0].url === null) {  //如果是MV 电台 广告 之类的就提示播放出错,并返回首页
        wx.showModal({
          content: '服务器开了点小差~~',
          cancelColor: '#DE655C',
          confirmColor: '#DE655C',
          showCancel: false,
          confirmText: '返回',
          complete() {
            wx.switchTab({
              url: '/pages/index/index'
            })
          }
        })
      } else {
        this.createBgAudio(res.data.data[0]);
      }
    }
  })
  //获取到歌曲音频,则显示出歌曲的名字,歌手的信息,即获取歌曲详情;如果失败,则播放出错。
  wx.request({
    url: API_BASE_URL + '/song/detail',
    data: {
      ids: audioId    //必选参数ids
    },
    success: res => {
      if (res.data.songs.length === 0) {
        wx.showModal({
          content: '服务器开了点小差~~',
          cancelColor: '#DE655C',
          confirmColor: '#DE655C',
          showCancel: false,
          confirmText: '返回',
          complete() {
            wx.switchTab({
              url: '/pages/index/index'
            })
          }
        })
      } else {
        this.setData({
          song: res.data.songs[0],  //获取到歌曲的详细内容,传给song
        })
        app.globalData.songName = res.data.songs[0].name;
      }
    },
  })
},
createBgAudio(res) {
  const bgAudioManage = wx.getBackgroundAudioManager(); //获取全局唯一的背景音频管理器。并把它给实例bgAudioManage
  app.globalData.bgAudioManage = bgAudioManage;         //把实例bgAudioManage(背景音频管理器) 给 全局
  bgAudioManage.title = 'title';                        //把title 音频标题 给实例
  bgAudioManage.src = res.url;                          // res.url 在createBgAudio 为 mp3音频  url为空,播放出错
  const history_songId = this.data.history_songId
  const historySong = {
    id: app.globalData.songId,
    songName:app.globalData.songName
  }
  history_songId.push(historySong)
  bgAudioManage.onPlay(res => {                         // 监听背景音频播放事件
    this.setData({
      isPlay: true,
      history_songId
    })
  });
  bgAudioManage.onEnded(() => {                  //监听背景音乐自然结束事件,结束后自动播放下一首。自然结束,调用go_lastSong()函数,即歌曲结束自动播放下一首歌
    this.go_lastSong();
  })
  wx.setStorageSync('historyId', history_songId); //把historyId存入缓存
},
})
复制代码

暂停/播放

<!-- 暂停播放图标 -->
<view class="play_suspend">
  <view class="icon_playing"><image bindtap="handleToggleBGAudio" src="../images/suspend.png" hidden="{{!isPlay}}" class="{{'img_play_suspend'}}" />  <!-- 暂停图标-->
  <image bindtap="handleToggleBGAudio" src="../images/play.png" hidden="{{isPlay}}" class="{{'img_play_suspend'}}" /></view> <!--播放图标-->
</view>
复制代码
// 播放和暂停
handleToggleBGAudio() {
  // const innerAudioContext = app.globalData.innerAudioContext;
  const bgAudioManage = app.globalData.bgAudioManage;
  const {isPlay} = this.data;
  if (isPlay) {
    bgAudioManage.pause();
    // innerAudioContext.pause();handleToggleBGAudio
  } else {
    bgAudioManage.play();
    // innerAudioContext.play();
  }
  this.setData({
    isPlay: !isPlay
  })
  console.log(this.data.isPlay)
}
复制代码

上一首/下一首(随机播放)

思路:点击歌单或歌手页,获取到对应的歌单/歌手id-> wx.request 请求数据获取到所有的歌单内/歌手热门歌曲音频地址->给全局变量globalData->点击上一首/下一首随机获取到全局变量的一则数据->给play()方法->播放

<!--歌单-->
  onLoad: function (options) {
  wx.showLoading({
    title: '加载中',
  });
  const sheetId = options.id;
  wx.request({
    url: API_BASE_URL + '/playlist/detail',
    data: {
      id: sheetId    
    },
    success: res => {
      const waitForPlay = new Array;
      for (let i = 0; i <= res.data.playlist.trackIds.length - 1;i++){ //循环打印出其id
        waitForPlay.push(res.data.playlist.trackIds[i].id) //循环push ID 到waitForPlay数组
        app.globalData.waitForPlaying = waitForPlay  //让waitForPlay数组给全局数组
      }
      wx.hideLoading()
      this.setData({
        songList: res.data.playlist.tracks
      })  
    }
  })
}
复制代码
<view class="icon_playing "><image src="../images/lastSong.png" class=" icon_play" bindtap="go_lastSong" /></view>
<view class="icon_playing "><image src="../images/nextSong.png" class=" icon_play" bindtap="go_lastSong" /></view>
复制代码
go_lastSong:function(){ 
  let that = this;
  const lastSongId = app.globalData.waitForPlaying;
  const songId = lastSongId[Math.floor(Math.random() * lastSongId.length)]; //随机选取lastSongId数组的一个元素
  that.data.songid = songId;
  this.play(songId)//传进play()方法中
  app.globalData.songId=songId;
}
复制代码

歌词/封面切换

仿网易云音乐微信小程序
因为网易云API的歌词接口崩溃,请求不到歌词,所以我只能把歌词写死为 纯音乐,请欣赏 。类似于 v-show

<!-- 封面 -->
<!-- 一开始onload时,showLyric=true, 显示为转动的图标,点击图标,切换为歌词-->
<view class="sing-show" bindtap="showLyric" >
  <view class="moveCircle {{isPlay ? 'play' : ''}}" hidden="{{!showLyric}}">
    <image src="{{song.al.picUrl}}" class="coverImg {{isPlay ? 'play' : ''}}" hidden="{{!showLyric}}"/>
  </view>
  <text  hidden="{{showLyric}}" class="songLyric">纯音乐,请欣赏</text>
</view>
复制代码
// 点击切换歌词和封面
showLyric(){
  const {showLyric} = this.data;
  this.setData({
    showLyric: !showLyric
  })
}
复制代码

破产版的孤独星球动效

仿网易云音乐微信小程序

封面旋转:

@keyframes rotate {
  0%{
    transform: rotate(0);
  }
  100%{
    transform: rotate(360deg);
  }
}
复制代码

扩散的圆形线条: 其实就是外面套一个盒子,盒子边框变大以及透明度逐渐变低。

@keyframes moveCircle {
  0%{
    width: 400rpx;
    height: 400rpx;
    border: 1px solid rgba(255, 255, 255, 1)
  }
  30%{
    width: 510rpx;
    height: 510rpx;
    border: 1px solid rgba(255, 255, 255, 0.8)
  }
  50%{
    width: 610rpx;
    height: 610rpx;
    border: 1px solid rgba(255, 255, 255, 0.6)
  }
  80%{
    width: 700rpx;
    height: 700rpx;
    border: 1px solid rgba(255, 255, 255, 0.4)
  }
  99%{
    width: 375px;
    height: 375px;
    border: 1px solid rgba(255, 255, 255, 0.1)
  }
  100%{
    width: 0px;
    height: 0px;
    border: 1px solid rgba(255, 255, 255, 0)
  }
}
复制代码

背景毛玻璃

<!-- play.wxml -->
<image src="{{song.al.picUrl}}" class="background_img" ></image>
复制代码
/* 播放界面毛玻璃效果 */
.background_img{ 
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 100%;
  filter: blur(20px);
  z-index: -1;
  transform: scale(1.5); /*和网易云音乐对比了一下,发现也是放大1.5倍*/
}
复制代码

播放tabBar

仿网易云音乐微信小程序
思路是参考酷狗音乐小程序。这个tabBar的js,wxml与播放功能界面的js,wxml相同。因为音乐播放是用 wx.getBackgroundAudioManager()

背景音频播放器管理的,所以才能同步。

我的tabBar

仿网易云音乐微信小程序

播放历史

思路:play.js中一旦播放成功就把歌名及歌曲ID传入全局变量->push到play.js里的数组中-> wx.setStorageSync 把数据存入缓存->在需要的页面 wx.getStorageSync 获取到缓存。

<!--play.js-->
const history_songId = this.data.history_songId
const historySong = {
      // id: res.id
      id: app.globalData.songId,
      songName:app.globalData.songName
    }
    history_songId.push(historySong)
    wx.setStorageSync('historyId', history_songId); //把historyId存入缓存
复制代码
<!--me.js-->
 onShow:function(){
    var history = wx.getStorageSync('historyId');
    // console.log(history)
     this.setData({
      hidden:true,
      //  historyId: app.globalData.songName
       historyId: history
    })
    console.log(this.data.historyId)
  }
复制代码

结语

做项目的过程总的来说痛并快乐,因为改不出BUG的样子真的很狼狈,但实现了某一个功能的那一刻真的很欣慰。再次感谢给予帮助的老师学长同学。如果你喜欢这篇文章或者可以帮到你,不妨点个赞吧!同时也非常希望看到这篇文章的你在下方给出建议!最后奉上 源码 ,有需要的可以自取。最后,说点题外话,因为我是2020届毕业生,现在面临实习压力,有没有大佬捞一下。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

HTML5

HTML5

Matthew David / Focal Press / 2010-07-29 / USD 39.95

Implement the powerful new multimedia and interactive capabilities offered by HTML5, including style control tools, illustration tools, video, audio, and rich media solutions. Understand how HTML5 is ......一起来看看 《HTML5》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器