美文网首页
仿网易云音乐微信小程序

仿网易云音乐微信小程序

作者: 努力努力再努力_y | 来源:发表于2019-07-22 16:09 被阅读0次

    前言

    前段闲暇时间想了解一下网易云音乐的加密方式,想写个具体实现,flutter我放弃了,还是小程序简单点,即衍生出该项目,纯原生书写,仿安卓V6.25版本。网易云音乐加密方式稍后更新。

    开发前的准备

    • VScode代码编辑器。
    • 微信开发者工具
    • Android网易云音乐(V6.25版本)
    • 网易云音乐API(node版本,这可能是github上最全的了,手动点赞)
    • 本人复写Java版本个别接口(若需)
    • 阿里巴巴矢量图标库
    注:项目中http请求都是使用wx.request且未进行API封装,不是很优雅,有兴趣可自行封装

    已实现功能及展示截图(GIF)
    一、首页
    首页
    项目中主要使用弹性盒子布局

    首页主要加入了scroll-view滑动区分四个模块,目前只实现其中一个

    首页数据有(若需电台、MV数据可自行添加):

    • banner 轮播图
    • 推荐歌单
    • 推荐新音乐
    • 新碟
        /**
         * 获取轮播图数据
         */
        getBanner() {
            let that = this;
            wx.request({
                url: baseUrl + 'banner',
                header: {
                    'Content-Type': 'application/json'
                },
                success: function (res) {
                    // console.log(res);
                    if (res.data.code == 200) {
                        that.setData({
                            banner: res.data.banners
                        })
                    }
                }
            })
        },
    
        /**
         * 获取歌单信息
         */
        getPersonalized() {
            let that = this;
            wx.request({
                url: baseUrl + 'personalized?limit=6',
                header: {
                    'Content-Type': 'application/json'
                },
                success: function (res) {
                    if (res.data.code == 200) {
                        let pageData = res.data.result;
                        // 播放量四舍五入精确到万
                        pageData.forEach(function (item, index) {
                            item.playCount = (item.playCount / 10000).toFixed(0)
                        })
                        that.setData({
                            personalized: pageData
                        })
    
                    }
                }
            })
        },
    
        /**
         * 推荐新音乐
         */
        getNewsong() {
            let that = this;
            wx.request({
                url: baseUrl + 'personalized/newsong',
                header: {
                    'Content-Type': 'application/json'
                },
                success: function (res) {
                    // console.log(res.data.result);
                    if (res.data.code == 200) {
                        that.setData({
                            newsong: res.data.result
                        })
                    }
                }
            })
        },
    
        /**
         * 获取新碟信息
         */
        getNewest() {
            let that = this;
            wx.request({
                url: baseUrl + 'album/newest',
                header: {
                    'Content-Type': 'application/json'
                },
                success: function (res) {
                    // console.log(res);
                    if (res.data.code == 200) {
                        that.setData({
                            newest: res.data.albums
                        })
                    }
                }
            })
        },
    
    wxml页面渲染即可,具体实现详见page/index

    二、搜索

    搜索

    这一块实现也是花费了不少时间(手动吐槽)
    (1)数据项比较多,样式需要细调
    (2)scroll-view超出屏幕宽度之外的滑动(写完才发现尴尬问题)
    (3)swiper高度问题,这玩意太烦了,导致onReachBottom事件只触发一次,最后偷鸡解决了

    功能详解
    1. 搜索建议
      给input绑定实时监测输入框数据项(bindinput="inputext"),不为空时发送http请求
        searchSuggest() {
            let that = this;
            wx.request({
                url: baseUrl + 'search/suggest?keywords=' + this.data.searchKey + "&type=mobile",
                header: {
                    'Content-Type': 'application/json'
                },
                success: function (res) {
                    // console.log(res)
                    if (res.data.code == 200) {
                        that.setData({
                            searchsuggest: res.data.result.allMatch
                        })
                    }
                }
            })
        },
    
    1. 搜索历史
      当用户点击搜索、搜索历史、热搜榜数据进行搜索时将数据存储到StorageSync中
          if (key != '') {
                let history = wx.getStorageSync("history") || [];
                if (history.includes(key)) {
                    for (var i = 0; i < history.length; i++) {
                        if (key == history[i]) {
                            history.splice(i, 1);
                        }
                    }
                }
                history.push(key)
                wx.setStorageSync("history", history);
            }
    
    1. 热搜榜(请求渲染即可)
        getSearchHotDetail() {
            let that = this;
            wx.request({
                url: baseUrl + 'search/hot/detail',
                header: {
                    'Content-Type': 'application/json'
                },
                success: function (res) {
                    wx.hideLoading()
                    // console.log(res.data);
                    if (res.data.code == 200) {
                        that.setData({
                            searchHotDetail: res.data.data
                        })
                    }
                }
            })
        },
    
    1. 搜索事件:详见searhFinput方法

    2. 上划加载更多
      swiper高度问题,导致至加载一次,后改用scroll-view的bindscrolltolower="loadMore"来实现

    具体实现详见page/search

    三、歌单

    歌单

    这块还是页面书写为注,毕竟非专业前端,比较费劲,具体请求数据有


       // 全部歌单
      gplaylist: function (isadd) {
        //分类歌单列表
        var that = this;
        // console.log(that.data.catelist.checked.name)
        wx.request({
          url: baseUrl + 'top/playlist',
          data: {
            limit: that.data.playlist.limit,
            offset: that.data.playlist.offset,
            cat: that.data.catelist.checked.name
          },
          complete: function (res) {
            // console.log(res)
            that.data.playlist.loading = true;
            if (!isadd) {
              that.data.playlist.list = res.data
            } else {
              res.data.playlists = that.data.playlist.list.playlists.concat(res.data.playlists);
              that.data.playlist.list = res.data
            }
            that.data.playlist.offset += res.data.playlists.length;
            that.setData({
              loading: false,
              playlist: that.data.playlist
            })
          }
        })
      },
    
      // 歌单选择
      cateselect: function (e) {
        var t = e.currentTarget.dataset.catype;
        this.data.catelist.checked = t
        this.setData({
          playlist: {
            list: {},
            offset: 0,
            limit: 30
          },
          loading: true,
          cateisShow: !this.data.cateisShow,
          catelist: this.data.catelist
        });
        this.gplaylist();
      }, 
    
      // 数据初始化
      init: function () {
        var that = this
        wx.request({
          url: baseUrl + 'playlist/catlist',
          complete: function (res) {
            that.setData({
              catelist: {
                isShow: false,
                res: res.data,
                checked: res.data.all
              }
            })
          }
        })
      },
    
    具体实现详见page/gedanSquare

    四、排行榜

    排行榜

    排行榜类型我是根据name来分类的(略微尴尬),榜单较多,就不一个个找id了

        /**
       * 所有榜单内容摘要
       */
      getToplistDetail() {
        let that = this;
        wx.request({
          url: baseUrl + 'toplist/detail',
          header: {
            'Content-Type': 'application/json'
          },
          success: function(res) {
            if (res.data.code == 200) {
              var list = res.data.list;
              var officialList = []
              var recommendationList = []
              var globalList = []
              var moreList = []
              for (var index in list) {
                var name = list[index].name
                if (name == "云音乐飙升榜" || name == "云音乐新歌榜" || name == "网易原创歌曲榜" || name == "云音乐热歌榜") {
                  officialList.push(list[index])
                } else if (name == "江小白YOLO云音乐说唱榜" || name == "说唱TOP榜" || name == "云音乐电音榜" || name == "云音乐ACG音乐榜" || name == "云音乐欧美新歌榜" || name == "抖音排行榜") {
                  recommendationList.push(list[index])
                } else if (name == "美国Billboard周榜" || name == "UK排行榜周榜" || name == "Beatport全球电子舞曲榜" || name == "日本Oricon周榜" || name == "iTunes榜" || name == "香港电台中文歌曲龙虎榜") {
                  globalList.push(list[index])
                } else {
                  moreList.push(list[index])
                }
              }
              that.setData({
                officialList,
                recommendationList,
                globalList,
                moreList,
                loading: false,
              })
            }
          }
        })
      },
    
    此处有一个地方需要注意:

    查看排行榜详情中,node接口作者将id简化做了一层转意,某些榜单无法查看详情
    解决方案,使用本人覆写Java版本,点击事件改为openTopList即可



    具体实现详见page/rankingList

    五、新歌速递

    新歌速递

    调用接口渲染即可,数据较多,小程序渲染有卡顿,小伙伴们自行优化



    书写仓促代码未优化(回头看重复代码有点多)
    为了播放正常,此处数据项需要做一致性处理(没办法,先写的歌单播放,返回数据格式不同)


    具体实现详见page/newsong

    六、MV

    mv

    这边是区分两类数据的歌手的MV及用户上传的视频需要注意


        /**
         * 获取MV数据
         */
        getMvDetail(id) {
            let that = this;
    
            wx.request({
                url: baseUrl + 'mv/detail?mvid=' + id,
                header: {
                    'Content-Type': 'application/json'
                },
                success: function (res) {
                    // console.log(res);
                    if (res.data.code == 200) {
                        that.setData({
                            mvDetail: res.data.data,
                            mvId: id
                        })
                    }
                }
            })
        },
    
        /**
         * 获取video数据
         */
        getVideoDetail(id) {
            let that = this;
            wx.request({
                url: baseUrl + 'video/detail?id=' + id,
                header: {
                    'Content-Type': 'application/json'
                },
                success: function (res) {
                    if (res.data.code == 200) {
                        that.setData({
                            'mvDetail.cover': res.data.data.coverUrl,
                            'mvDetail.name': res.data.data.title,
                            'mvDetail.publishTime': util.formatTimeCommit(res.data.data.publishTime, 3),
                            'mvDetail.desc': res.data.data.description,
                            'mvDetail.playCount': res.data.data.playTime,
                            'mvDetail.likeCount': res.data.data.praisedCount,
                            'mvDetail.subCount': res.data.data.subscribeCount,
                            'mvDetail.commentCount': res.data.data.commentCount,
                            'mvDetail.shareCount': res.data.data.shareCount,
                            mvId: id
                        })
                    }
                }
            })
        },
    
    具体实现详见page/mv

    七、评论


    (1)分歌单评论及歌曲评论,注意区分请求
    (2)评论中emoji需要转换才能显示,详见具体方法


    具体实现详见page/comment
      getPlaylistComment(id) {
        console.log("歌单评论:" + id)
        let that = this;
        wx.request({
          url: baseUrl + 'comment/playlist?id=' + id,
          header: {
            'Content-Type': 'application/json'
          },
          success: function (res) {
            // 评论表情与日期需要转换(!!!)
            var data = res.data;
            for (let i in data.hotComments) {
              data.hotComments[i].time = util.formatTimeCommit(data.hotComments[i].time, 2);
              data.hotComments[i].content = util.emoji(data.hotComments[i].content)
              if (data.hotComments[i].beReplied[0]) {
                data.hotComments[i].beReplied[0].content = util.emoji(data.hotComments[i].beReplied[0].content)
              }
            }
            for (let i in data.comments) {
              data.comments[i].time = util.formatTimeCommit(data.comments[i].time, 2);
              data.comments[i].content = util.emoji(data.comments[i].content)
              if (data.comments[i].beReplied[0]) {
                data.comments[i].beReplied[0].content = util.emoji(data.comments[i].beReplied[0].content)
              }
            };
            that.setData({
              hotComments: data.hotComments,
              comments: data.comments,
              total: data.total,
              loading: false
            })
    
            wx.setNavigationBarTitle({
              title: '评论(' + (data.total || 0) + ")"
            })
          }
        })
      },
    
    
    具体实现详见page/comment

    八、歌曲播放

    歌曲播放

    项目核心功能,写(抄)的不是很优秀,自行领悟

    里面用到了微信小程序-通知广播WxNotificationCenter,别漏掉了!!!

    具体实现详见page/player

    九、播放栏


    播放栏使用了模板template,在项目中template/pageplay中有具体样式
    在需要的地方引入即可,别忘了通知广播,否则不能同步状态




    目前只在首页及歌单页面添加了,如有需要请参照歌单页面方式进行添加。


    写在最后

    非专业前端,css可能不是很优雅,时间有限,小程序未进行优化。
    本人第一个较完整微信小程序,耗时较长,再加上工作比较繁忙,所以暂不打算更新了,感兴趣的小伙伴自行完善。

    数据源至
    node版本:https://github.com/Binaryify/NeteaseCloudMusicApi
    Java版本:本人覆写移步至https://www.jianshu.com/p/bb9ed6ef41b6

    小程序参考
    https://github.com/sqaiyan/NeteaseMusicWxMiniApp
    https://github.com/zhongjunhaoz/CloudMusic

    最后奉上源码wx-neteaseCloudMusic如果此项目对你有所帮助,麻烦给star吧。感谢!!!

    本项目仅供学习参考,侵权删!!!

    相关文章

      网友评论

          本文标题:仿网易云音乐微信小程序

          本文链接:https://www.haomeiwen.com/subject/axywlctx.html