微信小程式實戰教程:模仿—網易雲音樂(一)
初窺
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
<!-- 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為內容面板
//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
<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
呼叫例子:
具體參考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
})
})