资讯专栏INFORMATION COLUMN

微信小程序音乐播放器,leancloud后端支持,体验小程序数据绑定,Promise较为优雅解决回调

go4it / 2594人阅读

摘要:初窥添加音乐到收藏最近列表歌词滚动从一个开始微信开发者工具生成目录如下主页日志页面工具大体为每一个即是一个页面文件,每个页面有一个文件规定描述页面的这四个文件必须具有相同的路径与文件名。


初窥



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



            
                
                    
                        {{item}}
                    
                
                
            
            
                
                
                
            
 
       

block渲染data里面的四个tabs,slider为激活tab选项时候的表现,panel为内容面板

//js
var sliderWidth = 96; // 需要设置slider的宽度,用于计算中间位置
Page({
    data: {
        tabs: ["选项一", "选项二", "选项三"],
        activeIndex: 1,
        sliderOffset: 0,
        sliderLeft: 0
    },
    onLoad: function () {
        var that = 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

        
            
                
                    
                    
                    
                        
                    
                
                
            
            
        
        
            
                
                    实时搜索文本
                
            
        

一个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.c...海阔天空

http://neteasemusic.leanapp.c...


具体参考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
var app = getApp()
Page({
    data: {
        searchReault: []
    },
    //绑定事件
    inputTyping: function (e) {
        let that = this
        console.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) {
                let temp = []
                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"


    
       
          {{item.name}}
               {{item.singer}}
            
         
       

在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

//滑动 歌曲快进
function sliderToseek(e, cb) {
  wx.getBackgroundAudioPlayerState({
    success: function (res) {
      var dataUrl = res.dataUrl
      var duration = res.duration
      let val = e.detail.value
      let cal = val * duration / 100
      cb && 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]

// 获取歌词
function getlyric(id,cb) {
  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: {}, // 设置请求的 header
    success: function (res) {
      // success
    
      if (!res.data.lrc.lyric) return false;
     
      let lyric = res.data.lrc.lyric
     
      let timearr = lyric.split("[")
      let obj = {}
      let lyricArr=[]
      // seek 为键  歌词为value
      timearr.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("
")[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进行渲染:


添加歌曲:


我的可以在本地缓存中添加两个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.data
        var id =  value.id
        if (value) {
          // 设置到data
          that.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.duration
                    console.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 图片url
    playMusic: 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: {}, // 设置请求的 header
                success: function (res) {
                    // success
                    if (!res.data.lrc.lyric) return false;
                    let lyric = res.data.lrc.lyric
                    let timearr = lyric.split("[")
                    let obj = {}
                    let lyricArr = []
                    // seek 为键  歌词为value
                    timearr.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("
")[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.duration
        console.log("get bg success", tempduration, data)
        // 设置时长
        that.setData({
          sumduration: tempduration
        })
      })
},

这样即可缩减部分代码。

有帮助可以Star>github源码

18届小前端求职中["html/html5", "css/css3", "js/es5/es6", "node"]
1424254461@qq.com

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/82972.html

相关文章

  • 程序资源汇总

    awesome-github-wechat-weapp 是由OpenDigg整理并维护的微信小程序开源项目库集合。我们会定期同步上的项目到这里,也欢迎各位 UI组件开发框架实用库开发工具服务端项目实例Demo UI组件 weui-wxss ★1873 - 同微信原生视觉体验一致的基础样式库zanui-weapp ★794 - 好用易扩展的小程序 UI 库wx-charts ★449 - 微信小程...

    Olivia 评论0 收藏0
  • 300行代码写一个音乐放器程序

    摘要:项目主要是微信小程序也用到了等。前端部分主要是歌曲播放控制和交互部分的代码,更多关于小程序的内容可见微信小程序开发文档小程序框架结构微信小程序的入口是根目录下的它们分别描述的小程序的主题逻辑和公共配置部分。 刚进公司不久,因为公司部门年后业务拓展的关系,可能在年后会被分配到公司的微信公众号组做小程序相关的开发工作,因此写了个微信小程序wx-audio踩坑。目前还有一些功能没有写完:如返...

    chenatu 评论0 收藏0
  • 程序使用音乐api的方法,以及程序播放背景音乐失败的解决方案汇总

    摘要:下一步准备使用网易云代替音乐。已经开发新的网易云代替音乐了,需要的可以看看这篇文章为微信小程序开发的网易云音乐库 项目要做一个可以为日记添加音乐的小程序,所以要用到音乐api,参考了一些文章后我们封装了一个qq音乐api库(完成了动态token获取,音乐搜索,音乐专辑图片,音乐名称,歌手名称,播放),有需要的可以到Github自提。 小程序qq音乐api库Gihub地址https://...

    Sleepy 评论0 收藏0
  • 前端面试题总结(js、html、程序、React、ES6、Vue、算法、全栈热门视频资源)

    摘要:并总结经典面试题集各种算法和插件前端视频源码资源于一身的文档,优化项目,在浏览器端的层面上提升速度,帮助初中级前端工程师快速搭建项目。 本文是关注微信小程序的开发和面试问题,由基础到困难循序渐进,适合面试和开发小程序。并总结vue React html css js 经典面试题 集各种算法和插件、前端视频源码资源于一身的文档,优化项目,在浏览器端的层面上提升速度,帮助初中级前端工程师快...

    pumpkin9 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<