小程序有兩個(gè)頁(yè)面,主頁(yè)與播放頁(yè),因?yàn)椴捎昧薼eanCloud作為后臺(tái)數(shù)據(jù)開(kāi)發(fā),所以有一個(gè)lib包
樹(shù)結(jié)構(gòu),上圖 附微信小程序使用leanCloud鏈接
index頁(yè)的功能描述:提供音樂(lè)查找與選擇,搜索框不輸入點(diǎn)擊搜索得到數(shù)據(jù)庫(kù)中所以音樂(lè)文件(.mp3格式),支持對(duì)歌名或歌手的模糊查詢;
點(diǎn)擊列表中的某一首即可跳轉(zhuǎn)至播放頁(yè)進(jìn)行播放,從播放頁(yè)清單回退至index頁(yè)時(shí),index頁(yè)底部有播放小窗,點(diǎn)擊可回到播放頁(yè)
看這里:
代碼送上:index.wxml
<view class='theMain' style='background-image:url({{bgimage}});'> <view> <image class='image1'src="{{imageUrl}}" mode='aspectFit' wx:if="{{!searchTop}}"> </image> </view> <view class='page_row'> <view class="search"> <view class="df search_arr"> <icon class="searchcion" size='20' type='search'></icon> <input class="searchInput" placeholder="請(qǐng)輸入歌名,歌手" value="{{searchValue}}" bindinput='searchValueInput'/> </view> </view> <view class='sousuo' bindtap='sousuoButton'>搜索</view> </view> <view class='listBar'> <text class="search_top" style='width:80%;' wx:if="{{searchTop}}">搜索結(jié)果</text> </view> <view wx:for='{{json}}' class='music_list' bindtap='playTheMusic'data-name="{{item.name}}" data-url="{{item.url}}"> <text class='musictext'>{{item.name}}</text> <view class='url'>{{item.url}}</view> </view> <view class="search_no" wx:if="{{!centent_Show}}"> <text>暫時(shí)沒(méi)有庫(kù)存,聯(lián)系馮大神上傳哈</text> </view> <view class='littlebar' bindtap='littlebar'> <view class='littleImage' style='background-image:url({{imageUrl}});'></view> <view class='littleName'> <text class='songNameText'>{{songName}}</text> </view> </view> </view>
index.wxss這個(gè)css樣式代碼我覺(jué)得就沒(méi)啥可看的啦,這個(gè)還是看自己喜好調(diào),如果需要的話,文章最下方附有Github鏈接,
重要的還是在index.js上啦,一起來(lái)看看(我覺(jué)得不錯(cuò)的地方直接在代碼上標(biāo)注了,可以參考一下)
const AV = require("../../libs/av-weapp-min");//這里是對(duì)LeanCloud的引用,大家需要的話可問(wèn)度娘,很多詳細(xì)教程,我還是蠻喜歡這個(gè)工具的 var app=getApp() Page({ /** * 頁(yè)面的初始數(shù)據(jù) */ data: { imageUrl: "http://lc-9qxpppvr.cn-n1.lcfile.com/9c1af72ea0506789a9b9.jpg", searchValue:'', //搜索值 centent_Show: true, //這個(gè)可看wxml中的wx:if屬性,用來(lái)動(dòng)態(tài)顯示與隱藏 searchTop:false, //同上 bgimage: '', //背景圖片,在得到搜索結(jié)果的時(shí)候顯示 toName:'', songName:'' }, searchValueInput: function (e) {//得到搜索框的內(nèi)容并渲染到data下的searchValue中 var value = e.detail.value; this.setData({ searchValue: value, }); }, sousuoButton:function(){//對(duì)LeanCloud數(shù)據(jù)庫(kù)的查詢操作 var that=this; //這里非常重要,注意,此函數(shù)中then方法內(nèi)還有一個(gè)函數(shù),而該函數(shù)需要用到setData渲染數(shù)據(jù)。經(jīng)過(guò)兩層不可以直接用this.setData var nameQuery1 = new AV.Query('_File'); nameQuery1.contains('name', that.data.searchValue); var nameQuery2 = new AV.Query('_File'); nameQuery2.contains('name', '.mp3'); var query = new AV.Query.and(nameQuery1, nameQuery2);//這里相當(dāng)于數(shù)據(jù)庫(kù)where語(yǔ)句下的and操作 query.find().then(function (results) { // results is an array of AV.Object. //將data轉(zhuǎn)成json格式 //轉(zhuǎn)為數(shù)組 var jsonObj = JSON.parse(JSON.stringify(results)); app.globalData.musicList=jsonObj.concat(); //設(shè)置了全局變量,concat()方法為數(shù)組的復(fù)制,playing頁(yè)面需要。具體用法可問(wèn)度娘,app對(duì)象為頂部創(chuàng)建的對(duì)象 if (jsonObj.length == 0) { that.setData({ centent_Show: false, }); return; } that.setData({ json: app.globalData.musicList, searchTop:true, bgimage: "http://lc-9qxpppvr.cn-n1.lcfile.com/9c1af72ea0506789a9b9.jpg", centent_Show:true }); console.log(that.data.c) }, function (error) { console.log(error); // error is an instance of AVError. }); }, //點(diǎn)擊底部音樂(lè)bar進(jìn)入play界面 littlebar: function () { //這里有一個(gè)較難解決的問(wèn)題,我會(huì)在下面單獨(dú)寫(xiě)出,大神要是有思路請(qǐng)賜教,畢竟我才入門(mén)4天,很多都不懂 var pages=getCurrentPages(); var playingPage=pages[pages.length-2]; playingPage.setData({ angle:app.globalData.angle, }) wx.navigateBack(); }, //點(diǎn)擊清單跳轉(zhuǎn)到播放界面 //data-name="{{item.name}}" data-url="{{item.url}}" //只有在列表渲染的view控件中設(shè)置這些屬性,該函數(shù)才可得到點(diǎn)擊后對(duì)應(yīng)的屬性(可見(jiàn)上面的index.wxml) playTheMusic:function(e){
console.log(e.currentTarget.dataset.name); //一個(gè)調(diào)試方法,調(diào)試器輸出點(diǎn)擊的歌曲名 this.setData({ toName: e.currentTarget.dataset.name }); var songUrl = e.currentTarget.dataset.url; var songName = e.currentTarget.dataset.name; app.globalData.songName = songName; var theUrl = "../playing/playing?songUrl=" + songUrl + "&songName=" + songName //url攜帶參數(shù) wx.redirectTo({ //此種跳轉(zhuǎn)當(dāng)前頁(yè)面數(shù)據(jù)會(huì)保存在頁(yè)面棧中,可以回退,可問(wèn)度娘 url: theUrl, })
}, /** * 生命周期函數(shù)--監(jiān)聽(tīng)頁(yè)面加載 */ onLoad: function (options) { /** * 監(jiān)聽(tīng)音樂(lè)播放 */ wx.onBackgroundAudioPlay(function () { // callback console.log('onBackgroundAudioPlay') }) }, /** * 生命周期函數(shù)--監(jiān)聽(tīng)頁(yè)面顯示 */ onShow: functionthis.setData({ songName: app.globalData.songName , json: app.globalData.musicList, searchTop: true, }); } })
好了,這里我想談一下在index.js中我提到的單獨(dú)講的內(nèi)容。情景在于微信小程序界面之間的跳轉(zhuǎn),使用wx.navigateTo()方法跳轉(zhuǎn)到另一個(gè)頁(yè)面時(shí)可以將當(dāng)前頁(yè)面存入頁(yè)面棧,再通過(guò)wx.navigateBack()方法對(duì)頁(yè)面棧出棧操作,可回退到當(dāng)前頁(yè)面,使用wx.redirectTo()直接跳轉(zhuǎn),不存入頁(yè)面棧。在這個(gè)程序中,index->playing->index->playing重復(fù)多次無(wú)法直接實(shí)現(xiàn)。這個(gè)問(wèn)題我想了很久還是沒(méi)有完美的將其解決。
我的暫時(shí)邏輯是:點(diǎn)擊對(duì)應(yīng)音樂(lè)使用wx.redirectTo()方法,不保存頁(yè)面棧,跳轉(zhuǎn)至playing頁(yè);在playing頁(yè)點(diǎn)擊list圖標(biāo)使用wx.navigateTo(),保存頁(yè)面棧,同時(shí)跳到index頁(yè),這時(shí)候會(huì)調(diào)用onShow()函數(shù),將保存的list信息渲染到index頁(yè)面上;點(diǎn)擊index底部的播放小框調(diào)用wx.navigateBack()方法回到playing頁(yè)(playing頁(yè)的數(shù)據(jù)較多,所以采用了這種邏輯)
哪位大哥有更好解決方法煩請(qǐng)賜教哈
接下來(lái)是playing頁(yè):(推薦使用wx.getBackgroundAudioManager()接口對(duì)音樂(lè)行為進(jìn)行操作)
此頁(yè)面需要完成播放、暫停、上一曲、下一曲、回到index頁(yè)、動(dòng)態(tài)顯示播放時(shí)間和總長(zhǎng)度(部分實(shí)現(xiàn))、快進(jìn)(還未實(shí)現(xiàn))
看界面:
然后上代碼了:
playing.wxml:圖標(biāo)來(lái)源于百度圖片,簡(jiǎn)單ps摳圖后傳至云端使用;中間專輯圖旋轉(zhuǎn)感謝這篇博客,其中的旋轉(zhuǎn)快慢,每次角度可通過(guò)調(diào)節(jié)常量值實(shí)現(xiàn),下面代碼有標(biāo)注
<view class='Main'> <view class='songNameView'> <text></text> <text class='songName'>{{name}}</text> </view> <view class='imageView' style="background-image: url({{imageUrl}});" animation="{{animationData}}"> </view> <view class="backIndex" bindtap='backIndex' style='background-image:url({{homeImage}})'></view> <view class='line'> <view class='nowView'> <text class='now'>{{cur}}</text> </view> <view class='theLine'></view> <view class='allTimeView'> <text class='allTime'>{{duration}}</text> </view> </view> <view class="button"> <view class='back MusicIcon' style="background-image: url({{backUrl}});" bindtap='theBack'></view> <view class='center MusicIcon' style="background-image: url({{playOrStopUrl}});" bindtap='play'></view> <view class='next MusicIcon' style="background-image: url({{nextUrl}});" bindtap='theNext'></view> </view> </view>
同樣,playing.wxss可在我的Github中查看
下面是展示playing.js的時(shí)刻,各種邏輯在代碼中直接標(biāo)注了
// pages/playing/playing.js const AV = require("../../libs/av-weapp-min"); var app = getApp() Page({ /** * 頁(yè)面的初始數(shù)據(jù) */ data: { name:'測(cè)試', url:'', imageUrl:'http://lc-9qxpppvr.cn-n1.lcfile.com/9c1af72ea0506789a9b9.jpg', homeImage:"http://lc-9qxpppvr.cn-n1.lcfile.com/a9603240aab63a7950b0.png", animationData: {}, isPlay:false, //播放標(biāo)志 thePosition:0, //用來(lái)保存暫停時(shí)播放位置 angle:0, //用來(lái)不斷保存旋轉(zhuǎn)次數(shù),用于解決界面多次跳轉(zhuǎn)后旋轉(zhuǎn)失速問(wèn)題 cur:'--:--', //當(dāng)前時(shí)間 duration:'--:--' //總時(shí)長(zhǎng) }, //返回到清單頁(yè) //此處在上面有提及,點(diǎn)擊list圖標(biāo)到index頁(yè)面,并將跳轉(zhuǎn)前最后一個(gè)旋轉(zhuǎn)角度渲染給全局變量 backIndex: function () { wx.navigateTo({ url: '../index/index', }); app.globalData.angle=this.data.angle; }, /** * 生命周期函數(shù)--監(jiān)聽(tīng)頁(yè)面加載 */ onLoad: function (options) { //加載傳遞過(guò)來(lái)的參數(shù) //options為隨url傳遞過(guò)來(lái)的參數(shù) this.setData({ name: options.songName, url: options.songUrl, imageUrl:"http://lc-9qxpppvr.cn-n1.lcfile.com/9c1af72ea0506789a9b9.jpg", backUrl:"http://lc-9qxpppvr.cn-n1.lcfile.com/2574f1888750f8eaea88.png", nextUrl:"http://lc-9qxpppvr.cn-n1.lcfile.com/299a6353324cb312b00e.png", playOrStopUrl:"http://lc-9qxpppvr.cn-n1.lcfile.com/56885a2b7b3e478dd0c6.png", isPlay:true }) //加載頁(yè)面時(shí)執(zhí)行播放動(dòng)作 wx.playBackgroundAudio({ dataUrl: this.data.url, }) }, //播放/暫停 play:function(){ const backgroundAudioManager = wx.getBackgroundAudioManager(); var theTime; var allTime; if(this.data.isPlay){ wx.pauseBackgroundAudio(); //推薦都使用這個(gè)API,我之前不知道這個(gè)API,導(dǎo)致在后來(lái)的開(kāi)發(fā)中無(wú)法實(shí)現(xiàn)上面列舉的全部功能 theTime = backgroundAudioManager.currentTime;//不甘心,所以在點(diǎn)擊播放或暫停時(shí)可對(duì)頁(yè)面進(jìn)行時(shí)間的動(dòng)態(tài)渲染,也算是完成了一點(diǎn)吧 allTime = backgroundAudioManager.duration; var theString1 = theTime.toFixed(0); var theInt1 = parseInt(theString1); var m1 = theInt1 / 60; var mString1 = m1.toFixed(0); //截取小數(shù)點(diǎn)后0位數(shù)字,結(jié)果為String類型 var mInt1 = parseInt(mString1); //轉(zhuǎn)number var s1 = theInt1 % 60 / 100; var cur = mInt1 + s1; var theString = allTime.toFixed(0); var theInt = parseInt(theString); var m = theInt/60; var mString = m.toFixed(0); var mInt = parseInt(mString); var s = theInt%60/100; var all = mInt+s; this.setData({ playOrStopUrl:"http://lc-9qxpppvr.cn-n1.lcfile.com/22a26757fca8c46a2940.png",//替換為暫停圖標(biāo) isPlay: false, //渲染一些需要的數(shù)據(jù) thePosition: theTime, duration:all, cur: cur }); }else{ backgroundAudioManager.seek(this.data.thePosition); backgroundAudioManager.play(); this.setData({ playOrStopUrl: "http://lc-9qxpppvr.cn-n1.lcfile.com/56885a2b7b3e478dd0c6.png", isPlay:true }); } }, //下一首 theNext:function(){//對(duì)全局變量下的查找清單進(jìn)行操作,如果當(dāng)前歌曲為最后一首,跳轉(zhuǎn)到第一首 var j; var musicList = getApp().globalData.musicList.concat(); for (var i = 0; i < musicList.length;i++){ if(musicList[i].name==this.data.name){ j=i; break; }else{ j=-1; } } if (musicList.length-1==j){ this.setData({ name: musicList[0].name, url: musicList[0].url }); }else{ this.setData({ name: musicList[j+1].name, url: musicList[j + 1].url }); } wx.playBackgroundAudio({ dataUrl: this.data.url, }) }, //上一首 theBack:function(){//若為第一首,跳轉(zhuǎn)到最后一首 var j; var theLength=0; var musicList = getApp().globalData.musicList.concat(); theLength=musicList.length; for (var i = 0; i < musicList.length; i++) { if (musicList[i].name == this.data.name) { j = i; break; } else { j = 1; } } if (j==0) { this.setData({ name: musicList[theLength-1].name, url: musicList[theLength-1].url }); } else { this.setData({ name: musicList[j-1].name, url: musicList[j-1].url }); } wx.playBackgroundAudio({ dataUrl: this.data.url, }) },/** * 生命周期函數(shù)--監(jiān)聽(tīng)頁(yè)面顯示 */ onShow: function () {//這里為專輯圖旋轉(zhuǎn)函數(shù),調(diào)用wx.createAnimation()接口,更多參數(shù)詳情可看官方API var animation = wx.createAnimation({ duration: 1000, timingFunction: 'ease', }) this.animation = animation // animation.scale(2, 2).rotate(45).step() this.setData({ animationData: animation.export() }) var n = 0; //連續(xù)動(dòng)畫(huà)需要添加定時(shí)器,所傳參數(shù)每次+1就行 setInterval(function () { n=this.data.angle; if(this.data.isPlay){//暫停時(shí)停止旋轉(zhuǎn),播放旋轉(zhuǎn)的邏輯 n = n + 1; }else{ n=n; } this.setData({ angle: n, }) this.animation.rotate(8 * n).step()//8為每次轉(zhuǎn)8°,應(yīng)該是,個(gè)人喜好設(shè)置 this.setData({ animationData: this.animation.export() }) }.bind(this), 360)//360ms轉(zhuǎn)一個(gè)角度,個(gè)人喜好設(shè)置 } })
OK,到這里就可以實(shí)現(xiàn)一個(gè)演示視頻中所有功能的簡(jiǎn)易播放器了,是不是覺(jué)得很簡(jiǎn)單。