摘要:組件結(jié)構(gòu)接著我們就該搭建這個(gè)播放器的組件了??偟脑硎鞘紫全@取音頻的持續(xù)時(shí)間,然后通過(guò)一個(gè)定時(shí)器,不斷更新顯示時(shí)間,播放完成時(shí),計(jì)時(shí)器停止。這個(gè)頁(yè)面比較簡(jiǎn)單,播放器標(biāo)簽,綁定了事件,即播放完成后執(zhí)行。
這個(gè)播放器的開(kāi)發(fā)歷時(shí)2個(gè)多月,并不是說(shuō)它有多復(fù)雜,相反它的功能還非常不完善,僅具雛形。之所以磨磨蹭蹭這么久,一是因?yàn)橥涎?,二也是?shí)習(xí)公司項(xiàng)目太緊。8月底結(jié)束實(shí)習(xí)前寫(xiě)完了樣式,之后在家空閑時(shí)間多了,集中精力就把JS部分做完了。Muse-ui這個(gè)播放器確實(shí)比當(dāng)初構(gòu)想的復(fù)雜,開(kāi)始只打算做一個(gè)搜歌播放的功能?,F(xiàn)在做出來(lái)的這個(gè)播放器,可以獲取熱門(mén)歌曲,可以搜歌,可以調(diào)整播放進(jìn)度條,功能確實(shí)完善不少。
這次完成這個(gè)項(xiàng)目也是收獲頗豐,點(diǎn)了不少新的技能點(diǎn),當(dāng)然,這個(gè)簡(jiǎn)陋的小項(xiàng)目也挖了不少坑,不知道啥時(shí)候能填上……
話不多說(shuō),看代碼吧。
不記得在哪個(gè)網(wǎng)站看到這個(gè)組件庫(kù)的了,覺(jué)得好酷炫,于是用起來(lái)~
這是官網(wǎng):地址
使用這個(gè)組件庫(kù)的原因除了漂亮,還因?yàn)檫@是基于Vue 2.0,無(wú)縫對(duì)接,方便。
使用方法跟之前的插件一樣,npm安裝:
npm install --save muse-ui
安裝好后,在main.js中注冊(cè)。
import MuseUi from "muse-ui" import "muse-ui/dist/muse-ui.css" import "muse-ui/dist/theme-light.css" Vue.use(MuseUi)
就可以在項(xiàng)目中使用了。
PS:Muse-ui的icon是基于谷歌的Material icons,大家可以根據(jù)自己的需求到官網(wǎng)找icon的代碼。
接著我們就該搭建這個(gè)播放器的組件了。
結(jié)構(gòu)如下:
||-- player.vue // 主頁(yè)面 | |-- playerBox.vue // 播放器組件 | |-- popular.vue // 熱門(mén)歌曲頁(yè)面 | |-- songList.vue // 歌曲列表頁(yè)面 | |-- play.vue // 播放器頁(yè)面 | |-- search.vue // 搜索頁(yè)面
PS:熱門(mén)歌曲、搜索頁(yè)面都能進(jìn)入歌曲列表頁(yè)面,播放器組件playerBox.vue 是放標(biāo)簽的組件,是功能性組件。
我們來(lái)分別敘述:
1.player.vue直接看代碼吧:
解釋一下:
由于Muse-ui有部分樣式用到了less,所以在這里我們需要npm安裝一個(gè)less的依賴(lài),安裝好后即可使用。
npm install less less-loader --save
這里我們加載了一個(gè)底部導(dǎo)航,muse-ui的,官網(wǎng)可以查到相關(guān)代碼。這里要注意的是,為了讓用戶體驗(yàn)更好,我們需要讓我們的底部導(dǎo)航隨當(dāng)前路由變化而高亮。具體是用了一段JS代碼。
watch監(jiān)視路由變化并觸發(fā)一個(gè)method:changebar(),這個(gè)函數(shù)會(huì)獲取當(dāng)前的路由名,并把bottomNav的值設(shè)置為當(dāng)前路由名——即高亮當(dāng)前的路由頁(yè)面
playerBox.vue組件之所以放在主組件里,就是為了音樂(lè)在每一個(gè)子頁(yè)面都能播放,而不會(huì)因?yàn)樘D(zhuǎn)路由而停止播放。
2.popular.vue這是推薦歌單界面,這里用到了一個(gè)輪播圖插件,是基于vue的,使用起來(lái)比較方便,直接用npm安裝:
npm install vue-awesome-swiper --save
安裝好后,同樣在main.js中注冊(cè):
import VueAwesomeSwiper from "vue-awesome-swiper" Vue.use(VueAwesomeSwiper)
然后我們來(lái)看頁(yè)面的代碼:
iPlayer{{item.name}}
這里要說(shuō)明一下,上面的這些組件除了playerBox之外都要在main.js中注冊(cè)才能使用。注冊(cè)方法忘記的了話,回頭看看我之前寫(xiě)的todolist的項(xiàng)目是怎么注冊(cè)的。
在store.js中添加playList函數(shù):
playlist(state,id){ const url="http://localhost:3000/playlist/detail?id="+id; axios.get(url).then(res=> { state.playlist=res.data.playlist; }) },
這里的頁(yè)面mu開(kāi)頭的基本都是用Muse-ui搭建起來(lái)的,Swiper開(kāi)頭的則是輪播圖插件。界面不復(fù)雜,主要是三個(gè)部分,上面的輪播圖,中間的熱門(mén)歌單推薦,底部的版權(quán)信息。樣式基本是模板,這里做了一個(gè)簡(jiǎn)單的移動(dòng)端適配:在PC端歌單會(huì)以每排4個(gè)分兩排的形式排列,在移動(dòng)端歌單則會(huì)以每排2個(gè)分四排的形式排列,適配的方法是媒體查詢(xún),通過(guò)改變歌單div的寬度改變每行歌單的數(shù)目。
這里要注意的:
歌單的數(shù)據(jù)和輪播圖都是用的網(wǎng)易云數(shù)據(jù),所以沒(méi)有開(kāi)api是無(wú)法讀取的,引入axios的部分可以先不寫(xiě),也可以寫(xiě)好先放著。
這里methods和created里面的內(nèi)容都涉及到axios的請(qǐng)求,所以可以先不寫(xiě),不影響樣式呈現(xiàn)。數(shù)據(jù)可以先用假數(shù)據(jù)代替。
playList的目的是點(diǎn)擊歌單的時(shí)候,進(jìn)入歌單詳情頁(yè),同時(shí)根據(jù)傳遞進(jìn)去的歌單id獲取歌單的具體數(shù)據(jù),axios的地址是api的地址,需要加載api插件才能使用。
3.play.vue終于到了最核心的組件,之所以說(shuō)它核心是因?yàn)檫@是播放界面,音頻播放的長(zhǎng)度、音頻信息都會(huì)在這里被呈現(xiàn),而播放器的核心功能——播放——也是在這里被操作(播放/暫停)。
看具體代碼:
iPlayer{{audio.songName}} - {{audio.singer}}
store.js添加代碼:
play(state){ clearInterval(ctime); const playerBar=document.getElementById("playerBar"); const eve=$(".addPlus i")[0]; let currentTime=playerBar.currentTime; let currentMinute=Math.floor(currentTime/60)+":"+(currentTime%60/100).toFixed(2).slice(-2); let duraTime=playerBar.duration; let duraMinute=Math.floor(duraTime/60)+":"+(duraTime%60/100).toFixed(2).slice(-2); state.audio.progressPercent=((playerBar.currentTime/playerBar.duration)*100).toFixed(1); if(playerBar.paused){ playerBar.play(); eve.innerHTML="pause"; state.audio.duration=duraMinute; state.audio.currentTime=currentMinute; ctime=setInterval( function(){ currentTime++; currentMinute=Math.floor(currentTime/60)+":"+(currentTime%60/100).toFixed(2).slice(-2); state.audio.currentTime=currentMinute; state.audio.progressPercent=((playerBar.currentTime/playerBar.duration)*100).toFixed(1); },1000 ) }else { playerBar.pause(); eve.innerHTML="play_arrow"; clearInterval(ctime); } }, audioEnd(state){ const playerBar=document.getElementById("playerBar"); const eve=$(".addPlus i")[0]; eve.innerHTML="play_arrow"; clearInterval(ctime); playerBar.currentTime=0; let currentTime=playerBar.currentTime; let currentMinute=Math.floor(currentTime/60)+":"+(currentTime%60/100).toFixed(2).slice(-2); state.audio.currentTime=currentMinute; }, editProgress(state,progressValue){ const playerBar=document.getElementById("playerBar"); const eve=$(".addPlus i")[0]; let duraTime=playerBar.duration; let duraMinute=Math.floor(duraTime/60)+":"+(duraTime%60/100).toFixed(2).slice(-2); // console.log(progressValue); clearInterval(ctime); if(playerBar.paused){ playerBar.play(); eve.innerHTML="pause" state.audio.duration=duraMinute; } let currentTime=playerBar.duration*(progressValue/100); ctime=setInterval( function(){ currentTime++; currentMinute=Math.floor(currentTime/60)+":"+(currentTime%60/100).toFixed(2).slice(-2); state.audio.currentTime=currentMinute; state.audio.progressPercent=((playerBar.currentTime/playerBar.duration)*100).toFixed(1); },1000 ) playerBar.currentTime=currentTime; let currentMinute=Math.floor(currentTime/60)+":"+(currentTime%60/100).toFixed(2).slice(-2); state.audio.currentTime=currentMinute; },
如代碼所示,我在頂部導(dǎo)航添加了一個(gè)icon button,樣式來(lái)自Muse-ui綁定了一個(gè)點(diǎn)擊事件backpage,點(diǎn)擊后會(huì)回到上一個(gè)路由頁(yè)面。這個(gè)需要配合之前的高亮底部導(dǎo)航icon,才能實(shí)現(xiàn)返回上一路由的同時(shí)高亮相對(duì)應(yīng)的icon。
還要注意的是,computed里有兩個(gè)方法,第一個(gè)是獲取vuex里面的當(dāng)前曲目信息;第二個(gè)則是獲取進(jìn)度條的百分比信息,這個(gè)方法實(shí)現(xiàn)了數(shù)據(jù)的雙向綁定,隨著后臺(tái)設(shè)定的計(jì)時(shí)器,不斷地更新,從而實(shí)現(xiàn)播放時(shí)進(jìn)度條的變化。同樣,這里的樣式也是來(lái)自Muse-ui的Slider。
這里有一個(gè)需要注意的坑是,Muse-ui自帶了許多的函數(shù),第一次寫(xiě)的時(shí)候沒(méi)有注意,在進(jìn)度條上綁定了一個(gè)mouseup事件,結(jié)果無(wú)效,后來(lái)才發(fā)現(xiàn),其實(shí)已經(jīng)自帶了change事件,還可以實(shí)現(xiàn)移動(dòng)端的兼容。所以寫(xiě)代碼的時(shí)候一定要多看看官網(wǎng)文檔。
關(guān)于store.js里的方法,play是播放/暫停,具體會(huì)根據(jù)當(dāng)前音頻文件的paused(即是否暫停)來(lái)判斷??偟脑硎鞘紫全@取音頻的持續(xù)時(shí)間,然后通過(guò)一個(gè)定時(shí)器,不斷更新顯示時(shí)間,播放完成時(shí),計(jì)時(shí)器停止。
計(jì)時(shí)器很關(guān)鍵,進(jìn)度條和顯示時(shí)間的更新都需要它。但是計(jì)時(shí)器有個(gè)坑,如果把計(jì)時(shí)器聲明放在play方法里,則無(wú)法在audioEnd方法里停止計(jì)時(shí)器,所以這里我們需要在最外層先聲明一個(gè)ctime,然后再在play方法里把定時(shí)器賦值給ctime,這樣我們就可以隨時(shí)停止計(jì)時(shí)器了。
audioEnd方法是播放停止時(shí)要做的事情,我們會(huì)把停止按鈕切換成播放,把顯示時(shí)間修改掉,別忘了停止計(jì)時(shí)器。
editProgress方法是點(diǎn)擊或拖動(dòng)進(jìn)度條時(shí)做的事情,我們會(huì)改變當(dāng)前音頻的currentTime,即當(dāng)前時(shí)間,如果音頻是暫停狀態(tài),我們要讓它繼續(xù)播放。
4.search.vue這也是一個(gè)比較核心的一個(gè)功能,畢竟推薦的歌單只有幾個(gè)??创a:
iPlayer{{index+1}} {{item.artists[0].name}} - {{item.album.name}}
在store.js里添加:
getSearch(state,value){ const url="http://localhost:3000/search?keywords="+value+"?limit=30"; axios.get(url).then(res=>{ state.result=res.data.result; }) }, getSong(state,{id,name,singer,album,arid}){ const url="http://localhost:3000/music/url?id="+id; const imgUrl="http://localhost:3000/artist/album?id="+arid; const playerBar=document.getElementById("playerBar"); axios.get(url).then(res=>{ state.audio.location=res.data.data[0].url; state.audio.flag=res.data.data[0].flag; state.audio.songName=name; state.audio.singer=singer; state.audio.album=album; }) axios.get(imgUrl).then(res=>{ state.audio.picUrl=res.data.artist.picUrl; }) let currentTime=playerBar.currentTime; let currentMinute=Math.floor(currentTime/60)+":"+(currentTime%60/100).toFixed(2).slice(-2); let duraTime=playerBar.duration; let duraMinute=Math.floor(duraTime/60)+":"+(duraTime%60/100).toFixed(2).slice(-2); state.audio.duration=duraMinute; state.audio.currentTime=currentMinute; state.audio.progressPercent=((playerBar.currentTime/playerBar.duration)*100).toFixed(1); }
注意,在有需要使用axios的組件一定要import,npm下載安裝不用多說(shuō)了。
解釋一下這個(gè)組件的兩個(gè)方法:
getSearch是獲取搜索結(jié)果,它被綁定再搜索按鈕上,初始頁(yè)面是空白,通過(guò)傳遞關(guān)鍵字,用axios從api獲取搜索結(jié)果,再把結(jié)果顯示在頁(yè)面上。
getSong綁定在每一個(gè)搜索的結(jié)果上,有兩個(gè)步驟,第一是getSong,會(huì)把點(diǎn)擊的歌曲設(shè)置為要播放的歌曲,并把相關(guān)信息傳遞給play.vue,讓它顯示在相應(yīng)的地方;第二個(gè)步驟,會(huì)播放歌曲,也就是上面的play方法,具體不必再說(shuō)。
這里有一個(gè)坑,我們可能需要通過(guò)vuex傳遞參數(shù),但是有時(shí)候傳遞多個(gè)參數(shù)會(huì)出現(xiàn)undefined的情況,這時(shí)候我們要把參數(shù)們寫(xiě)成{參數(shù)一,參數(shù)二,參數(shù)三}的形式。
5.songList這個(gè)組件主要是歌單詳情頁(yè),基本的樣式和搜索頁(yè)一樣,就是獲取歌單的內(nèi)容不同,搜索頁(yè)面的列表是根據(jù)關(guān)鍵詞獲取的,歌單詳情頁(yè)的列表是根據(jù)歌單id獲取的,獲取的方式都是通過(guò)axios。
iPlayer{{playlist.name}} {{item.ar[0].name}} - {{item.al.name}}
沒(méi)什么需要解釋的,注意我們?cè)?b>getSong里面?zhèn)鬟f的多個(gè)參數(shù)。
6.playerBox.vue這個(gè)頁(yè)面比較簡(jiǎn)單,播放器audio標(biāo)簽,綁定了ended事件,即播放完成后執(zhí)行。
這里有一個(gè)坑,解釋一下:我把播放器按鈕放在這里了,為什么呢?之前我是放在play.vue里的,但是我發(fā)現(xiàn)一個(gè)問(wèn)題,就是通過(guò)點(diǎn)擊歌單的歌曲播放時(shí),無(wú)法改變播放/暫停按鈕,為什么呢?因?yàn)槲腋淖儼粹o的方法是用innerHTML改變,我為什么要用這種方法呢?因?yàn)镸use-ui的icon經(jīng)過(guò)渲染,是以標(biāo)簽的值的形式出現(xiàn)的。這就不得不獲取DOM了,但是如果把按鈕寫(xiě)在play.vue里,在歌單頁(yè)面時(shí)是獲取不到指定DOM的,因?yàn)楫?dāng)前頁(yè)面根本沒(méi)有這個(gè)DOM!只有把按鈕寫(xiě)在在主組件里的playerBox.vue里,才能獲取到指定DOM。
但是寫(xiě)在playBox.vue里又有一個(gè)問(wèn)題,按鈕會(huì)出現(xiàn)在每一個(gè)頁(yè)面里,但是我們只要它出現(xiàn)在播放頁(yè)面就好了,所以我們?cè)谶@里要給按鈕綁定一個(gè)v-show,里面的內(nèi)容就是判斷是不是在指定路由,如果是播放頁(yè)面,就顯示按鈕,不是,就隱藏按鈕。
axios和網(wǎng)易云apiaxios具體的配置我都在上面講了,這里介紹一款網(wǎng)易云的api和使用方法。
文檔在此
介紹一下使用方法,進(jìn)入git把它下下來(lái),在命令行執(zhí)行:
$ node app.js
在瀏覽器輸入地址:
localhost:3000
看到彈出的頁(yè)面就說(shuō)明服務(wù)器啟動(dòng)成功了。然后我們可以在文檔里查到具體請(qǐng)求的數(shù)據(jù),比如banner啊,歌單啊,搜索啊,都能請(qǐng)求。我們看到前面寫(xiě)的axios請(qǐng)求里的地址,都是具體請(qǐng)求的地址。
這里要注意的是,這個(gè)api默認(rèn)的是沒(méi)有開(kāi)啟跨域的,看app.js里有一段被隱藏的代碼就是跨域的相關(guān)設(shè)置,解除隱藏即可。
bug和未實(shí)現(xiàn)功能目前還存在一個(gè)比較大的bug,就是在歌單點(diǎn)擊播放時(shí),點(diǎn)擊第一次因?yàn)闆](méi)辦法獲取個(gè)去的url,無(wú)法播放,只有再點(diǎn)擊一次才能播放,這個(gè)bug暫時(shí)還沒(méi)有時(shí)間解決,會(huì)盡快解決。
然后目前還沒(méi)有實(shí)現(xiàn)的功能是播放列表,自然上一曲/下一曲按鈕也沒(méi)有用了,歌曲播放一遍也就停止了,這個(gè)功能不算難,抽空把它做出來(lái)。
參考資料這個(gè)app參考了一些技術(shù)文章,給了我很大的啟發(fā),附上鏈接。
用vue全家桶寫(xiě)一個(gè)“以假亂真”的網(wǎng)易云音樂(lè)
DIY 一個(gè)自己的音樂(lè)播放器 2.0 來(lái)襲
這個(gè)app前前后后,磨磨蹭蹭做了兩個(gè)月,好歹總算是做完了。學(xué)習(xí)還是得找項(xiàng)目來(lái)做,雖然這個(gè)項(xiàng)目還很簡(jiǎn)陋,但是還是get到很多知識(shí)點(diǎn),對(duì)于我的提高還是蠻大的。
這種項(xiàng)目不算難,寫(xiě)過(guò)的人也多,所以百分之八十的問(wèn)題都能百度出來(lái),剩下的百分之二十,技術(shù)社區(qū)里提個(gè)問(wèn)基本能夠解決。項(xiàng)目還是得自己寫(xiě)一遍,寫(xiě)的過(guò)程中才能發(fā)現(xiàn)問(wèn)題,也才能想辦法找到解決辦法,事情總是會(huì)比你想象的要簡(jiǎn)單一點(diǎn)。
項(xiàng)目不算大,但要一步步寫(xiě)下來(lái)總有可能有所遺漏,這里是我的GitHub,大家可以對(duì)照著看看有沒(méi)有遺漏。如果你喜歡我的項(xiàng)目,也希望star或者fork一波~
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/107099.html
摘要:基于等開(kāi)發(fā)一款移動(dòng)端音樂(lè),界面參考了安卓版的網(wǎng)易云音樂(lè)布局適配常見(jiàn)移動(dòng)端。圖標(biāo)使用阿里巴巴圖標(biāo)庫(kù),中間的唱片旋轉(zhuǎn)動(dòng)畫(huà)使用了實(shí)現(xiàn)。搜索功能實(shí)現(xiàn)功能搜索歌手歌單歌曲熱門(mén)搜索數(shù)據(jù)節(jié)流上拉刷新保存搜索記錄。 基于 Vue(2.5) + vuex + vue-router + vue-axios +better-scroll + Scss + ES6 等開(kāi)發(fā)一款移動(dòng)端音樂(lè) WebApp,UI ...
摘要:好看又好用的,專(zhuān)為以為原型,在技術(shù)棧上進(jìn)行實(shí)現(xiàn)。項(xiàng)目早在就已起步,起初是對(duì)的簡(jiǎn)單封裝?,F(xiàn)仍在持續(xù)維護(hù)和更新中。如果你在使用搭建自己心愛(ài)的小站,正想挑選一款好看又好用的音樂(lè)播放器,是少數(shù)不錯(cuò)的選擇。 Vue-APlayer showImg(https://segmentfault.com/img/remote/1460000013797187); showImg(https://segm...
閱讀 2984·2023-04-26 01:49
閱讀 2107·2021-10-13 09:39
閱讀 2322·2021-10-11 11:09
閱讀 951·2019-08-30 15:53
閱讀 2844·2019-08-30 15:44
閱讀 950·2019-08-30 11:12
閱讀 3023·2019-08-29 17:17
閱讀 2407·2019-08-29 16:57