todo:css
[ ] 添加音樂到收藏(最近)列表html
[ ] 歌詞滾動前端
微信開發者工具生成 目錄以下:html5
. |-- 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
node
app.json: 註冊pages window tabBar networkTimeout
組件說明
*.js: 做爲邏輯層 與wxml交互 有着豐富的
網絡,
媒體,
文件,
數據緩存,
位置,
設備,
界面...的api
官方文檔
*.wxml: 數據驅動的視圖層 + 微信提供了大量的組件 表單 導航 媒體 ...css3
weui爲小程序提供了 weui.wxcss 但大可能是造官方組件的輪子
這裏精選,也算是補充兩個經常使用組件
對於小程序沒有DOM操做 不熟悉mvvm思想的同窗 是個很好的入門git
navbar
es6
<!-- wxml --> <view class="weui-tab"> <view class="weui-navbar"> <block wx:for="{{tabs}}" wx:key="*this"> <view id="{{index}}" class="weui-navbar__item {{activeIndex == index ? 'weui-bar__item_on' : ''}}" bindtap="tabClick"> <view class="weui-navbar__title">{{item}}</view> </view> </block> <view class="weui-navbar__slider" style="left: {{sliderLeft}}px; transform: translateX({{sliderOffset}}px); -webkit-transform: translateX({{sliderOffset}}px);"></view> </view> <view class="weui-tab__panel"> <view class="weui-tab__content" hidden="{{activeIndex != 0}}">選項一的內容</view> <view class="weui-tab__content" hidden="{{activeIndex != 1}}">選項二的內容</view> <view class="weui-tab__content" hidden="{{activeIndex != 2}}">選項三的內容</view> </view> </view>
block渲染data裏面的四個tabs,slider爲激活tab選項時候的表現,panel爲內容面板github
//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顯示或隱藏web
searchbar
<view class="weui-search-bar"> <view class="weui-search-bar__form"> <view class="weui-search-bar__box"> <icon class="weui-icon-search_in-box" type="search" size="14"></icon> <input type="text" class="weui-search-bar__input" placeholder="搜索" value="{{inputVal}}" focus="{{inputShowed}}" bindinput="inputTyping" /> <view class="weui-icon-clear" wx:if="{{inputVal.length > 0}}" bindtap="clearInput"> <icon type="clear" size="14"></icon> </view> </view> <label class="weui-search-bar__label" hidden="{{inputShowed}}" bindtap="showInput"> <icon class="weui-icon-search" type="search" size="14"></icon> <view class="weui-search-bar__text">搜索</view> </label> </view> <view class="weui-search-bar__cancel-btn" hidden="{{!inputShowed}}" bindtap="hideInput">取消</view> </view> <view class="weui-cells searchbar-result" wx:if="{{inputVal.length > 0}}"> <navigator url="" class="weui-cell" hover-class="weui-cell_active"> <view class="weui-cell__bd"> <view>實時搜索文本</view> </view> </navigator> </view>
一個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
在此我將他部署到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"
<block wx:for="{{searchReault}}" wx:key="item" style="overflow-y: scroll;"> <navigator url="../now/index" class="weui-cell" hover-class="weui-cell_active" data-id="{{item.id}}" data-name="{{item.name}}" data-songUrl="{{item.mp3Url}}" data-picUrl="{{item.picUrl}}" data-singer="{{item.singer}}" open-type="switchTab" bindtap="tonow"> <view class="weui-cell__bd"> <view class="song-name">{{item.name}} <text class="song-singer">{{item.singer}}</text> </view> </view> </navigator> </block>
在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('\n')[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進行渲染:
<!--歌詞--> <view class="lyric-content" hidden="{{islyric}}" style="height:401px; overflow-y: scroll;" bindtap="showCircle"> <view class="lyric" style="overflow-y: scroll;"> <block wx:for="{{lyricArr}}" > <view> {{item}} </view> </block> </view> </view>
添加歌曲:
個人能夠在本地緩存中添加兩個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) } }) } }) },
這樣咱們不知不覺進入多個回調嵌套的問題
小程序暫時不支持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('\n')[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 }) }) },
這樣便可縮減部分代碼。
18屆小前端求職中['html/html5', 'css/css3', 'js/es5/es6', 'node']
1424254461@qq.com