成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

用Vue搭建一個(gè)應(yīng)用盒子(三):音樂(lè)播放器

appetizerio / 2678人閱讀

摘要:組件結(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部分做完了。

這個(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ō),看代碼吧。

Muse-ui

不記得在哪個(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的代碼。

組件結(jié)構(gòu)

接著我們就該搭建這個(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è)面的代碼:




  

這里要說(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ě)好先放著。

這里methodscreated里面的內(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),而播放器的核心功能——播放——也是在這里被操作(播放/暫停)。

看具體代碼:




  


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-uiSlider。

這里有一個(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:




  


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。




  

沒(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)易云api

axios具體的配置我都在上面講了,這里介紹一款網(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)襲

結(jié)語(yǔ)

這個(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

相關(guān)文章

  • Vue 實(shí)現(xiàn)網(wǎng)易云音樂(lè) WebApp

    摘要:基于等開(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 ...

    Karuru 評(píng)論0 收藏0
  • Vue 寫(xiě)出好看又好音樂(lè)放器 - Vue-APlayer

    摘要:好看又好用的,專(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...

    tomato 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<