用Vue搭建一個應用盒子(三):音樂播放器

這個播放器的開發歷時2個多月,並非說它有多複雜,相反它的功能還很是不完善,僅具雛形。之因此磨磨蹭蹭這麼久,一是由於拖延,二也是實習公司項目太緊。8月底結束實習前寫完了樣式,以後在家空閒時間多了,集中精力就把JS部分作完了。

這個播放器確實比當初構想的複雜,開始只打算作一個搜歌播放的功能。如今作出來的這個播放器,能夠獲取熱門歌曲,能夠搜歌,能夠調整播放進度條,功能確實完善很多。css

此次完成這個項目也是收穫頗豐,點了很多新的技能點,固然,這個簡陋的小項目也挖了很多坑,不知道啥時候能填上……html

話很少說,看代碼吧。vue

Muse-ui

不記得在哪一個網站看到這個組件庫的了,以爲好酷炫,因而用起來~html5

這是官網:地址node

使用這個組件庫的緣由除了漂亮,還由於這是基於Vue 2.0,無縫對接,方便。ios

使用方法跟以前的插件同樣,npm安裝:git

npm install --save muse-ui

安裝好後,在main.js中註冊。github

import MuseUi from 'muse-ui'
import 'muse-ui/dist/muse-ui.css'
import 'muse-ui/dist/theme-light.css'

Vue.use(MuseUi)

就能夠在項目中使用了。
PS:Muse-ui的icon是基於谷歌的Material icons,你們能夠根據本身的需求到官網找icon的代碼。web

組件結構

接着咱們就該搭建這個播放器的組件了。vuex

結構以下:

||-- player.vue       // 主頁面
|    |-- playerBox.vue   // 播放器組件
|    |-- popular.vue    // 熱門歌曲頁面
|        |-- songList.vue     // 歌曲列表頁面 
|    |-- play.vue    // 播放器頁面
|    |-- search.vue    // 搜索頁面

PS:熱門歌曲、搜索頁面都能進入歌曲列表頁面,播放器組件playerBox.vue 是放<audio>標籤的組件,是功能性組件。

咱們來分別敘述:

1.player.vue

直接看代碼吧:

<template>
  <div class="player">

    <!-- banner here-->
    <router-view></router-view>

    <!-- navbar here -->
    <mu-paper>
      <mu-bottom-nav :value="bottomNav" @change="handleChange">
        <mu-bottom-nav-item value="popular" title="流行" icon="music_note" to="/popular"/>
        <mu-bottom-nav-item value="play" title="播放" icon="play_arrow" to="/play"/>
        <mu-bottom-nav-item value="search" title="搜索" icon="search" to="/search"/>
      </mu-bottom-nav>
    </mu-paper>

    <!-- html5 player here -->
    <playerBox></playerBox>

  </div>
</template>

<script>

import playerBox from './playerBox.vue'

export default {



  name: 'player',
  data(){
    const pa=this.$route.path;
    const Pa=pa.slice(1);

    return{
      bottomNav: Pa
    }
  },
  components: {
    playerBox
  },
  methods:{
    handleChange (val) {
      this.bottomNav = val
    },
    changebar(){
      const va=this.$route.path;
      const Va=va.slice(1);
      this.bottomNav = Va
    }
  },
  watch:{
    "$route":"changebar"
  }
}
</script>
  

<style lang="less" >
  .mu-bottom-nav{
    position: fixed!important;
    bottom: 0px;
    background: #fafafa!important;
    z-index: 5;
    
  }
</style>

解釋一下:

  1. 因爲Muse-ui有部分樣式用到了less,因此在這裏咱們須要npm安裝一個less的依賴,安裝好後便可使用。

npm install less less-loader --save

  1. 這裏咱們加載了一個底部導航,muse-ui的,官網能夠查到相關代碼。這裏要注意的是,爲了讓用戶體驗更好,咱們須要讓咱們的底部導航隨當前路由變化而高亮。具體是用了一段JS代碼。

watch監視路由變化並觸發一個method:changebar(),這個函數會獲取當前的路由名,並把bottomNav的值設置爲當前路由名——即高亮當前的路由頁面

  1. playerBox.vue組件之因此放在主組件裏,就是爲了音樂在每個子頁面都能播放,而不會由於跳轉路由而中止播放。

2.popular.vue

這是推薦歌單界面,這裏用到了一個輪播圖插件,是基於vue的,使用起來比較方便,直接用npm安裝:

npm install vue-awesome-swiper --save

安裝好後,一樣在main.js中註冊:

import VueAwesomeSwiper from 'vue-awesome-swiper'

Vue.use(VueAwesomeSwiper)

而後咱們來看頁面的代碼:

<template>
  <div class="popular">

    <!-- navbar here -->
    <mu-appbar>
      <div class="logo">
        iPlayer
      </div>
    </mu-appbar>

    <!-- banner here-->
    <mu-card>
        <swiper :options="swiperOption">
          <swiper-slide v-for="(item,index) in banners" :key="index">
            <mu-card-media>
              <img :src="item.pic">
            </mu-card-media>
          </swiper-slide>
          <div class="swiper-pagination" slot="pagination"></div>
        </swiper>
    </mu-card>
    
    <div class="gridlist-demo-container" >
      <mu-grid-list class="gridlist-demo">
        <mu-sub-header>熱門歌單</mu-sub-header>
           <mu-grid-tile v-for="(item, index) in list" :key="index">
            <img :src="item.coverImgUrl"/>
            <span slot="title">{{item.name}}</span>
            <mu-icon-button icon="play_arrow" slot="action" @click="getListDetail(item.id)"/>
         </mu-grid-tile>
      </mu-grid-list>
    </div>
    
    <div class="footer-rights">
      <h4>版權歸Godown Huang全部,請<a href="https://github.com/WE2008311">聯繫我</a>。</h4>
    </div>


  </div>
</template>

<script>
import {swiper,swiperSlide} from 'vue-awesome-swiper'
import axios from 'axios'

export default {

  name: 'popular',
  data(){
    return{
      swiperOption: {
        pagination: '.swiper-pagination',
        paginationClickable: true,
        autoplay: 4000,
        loop:true
      },
      banners:[],
      list: []
    }
  },
  components: {
    swiper,
    swiperSlide
  },
  computed:{
    
  },
  created(){
    this.initPopular()
    
  },
  methods:{
    initPopular(){
      
      axios.get('http://localhost:3000/banner').then(res=> {
             this.banners=res.data.banners;
      }),
      axios.get('http://localhost:3000/top/playlist/highquality?limit=8').then(res=> {
             this.list=res.data.playlists;
      })
    },
    getListDetail(id){
      this.$router.push({path: '/songsList'})
      this.$store.commit('playlist',id);
    }
  }
}
</script>
  

<style lang="css">
  @media screen and (min-width: 960px){
    .mu-card-media>img{
      height: 400px!important;
    }
    .mu-grid-list>div:nth-child(n+2){
      width:25%!important;
    }
  }

  .mu-grid-tile>img{
    width: 100%;
  }

  .gridlist-demo-container{
    display: flex;
    flex-wrap: wrap;
    justify-content: space-around;
  }
  
  .gridlist-demo{
    width: 100%;
    overflow-y: auto;
  }
  .footer-rights>h4{
    color: #e1e1e1;
    font-weight: 100;
    font-size:.056rem;
    height:90px;
    padding-top: 10px;
    text-align: center;
  }
</style>
這裏要說明一下,上面的這些組件除了 playerBox以外都要在main.js中註冊才能使用。註冊方法忘記的了話,回頭看看我以前寫的todolist的項目是怎麼註冊的。

store.js中添加playList函數:

playlist(state,id){
        const url='http://localhost:3000/playlist/detail?id='+id;
        axios.get(url).then(res=> {
            state.playlist=res.data.playlist;
        })
    },

這裏的頁面mu開頭的基本都是用Muse-ui搭建起來的,Swiper開頭的則是輪播圖插件。界面不復雜,主要是三個部分,上面的輪播圖,中間的熱門歌單推薦,底部的版權信息。樣式基本是模板,這裏作了一個簡單的移動端適配:在PC端歌單會以每排4個分兩排的形式排列,在移動端歌單則會以每排2個分四排的形式排列,適配的方法是媒體查詢,經過改變歌單div的寬度改變每行歌單的數目。

這裏要注意的:

  1. 歌單的數據和輪播圖都是用的網易雲數據,因此沒有開api是沒法讀取的,引入axios的部分能夠先不寫,也能夠寫好先放着。
  2. 這裏methodscreated裏面的內容都涉及到axios的請求,因此能夠先不寫,不影響樣式呈現。數據能夠先用假數據代替。
  3. playList的目的是點擊歌單的時候,進入歌單詳情頁,同時根據傳遞進去的歌單id獲取歌單的具體數據,axios的地址是api的地址,須要加載api插件才能使用。

3.play.vue

終於到了最核心的組件,之因此說它核心是由於這是播放界面,音頻播放的長度、音頻信息都會在這裏被呈現,而播放器的核心功能——播放——也是在這裏被操做(播放/暫停)。

看具體代碼:

<template>
  <div class="play">

    <!-- navbar here -->
    <mu-appbar>
      <mu-icon-button icon="navigate_before" slot="left" v-on:click="backpage"/>
      <div class="logo">
        iPlayer
      </div>
    </mu-appbar>

    <!-- player here-->
    <div class="bgImg">
      <img :src="audio.picUrl" />
      <!-- 封面CD -->
      <mu-avatar  slot="left" :size="300" :src="audio.picUrl"/>
    </div>
    
    <div class="controlBar">
        <mu-content-block>
          {{audio.songName}} - {{audio.singer}}
        </mu-content-block>
        <div class="controlBarSlide">
          <span class="slideTime">{{audio.currentTime}}</span>
          <mu-slider v-bind:value="progressPercent" @change="editprogress" class="demo-slider"/>
          <span class="slideTime">{{audio.duration}}</span>
        </div>
        
    </div>


  </div>
</template>

<script>


export default {
  
  name: 'play',
  data(){
    return{
      
    }
  },
  components: {
    
  },
  computed:{
      audio(){
        return this.$store.getters.audio;
      },
      progressPercent(){
        return this.$store.getters.audio.progressPercent;
      }
  },
  methods:{
    backpage(){
      window.history.go(-1);
    },
    
    editprogress(value){
      
      this.$store.commit('editProgress',value)
    }
  }
}
</script>
  

<style lang="css">
  @media screen and (max-width: 414px){
    .bgImg .mu-avatar{
      height: 260px!important;
      width: 260px!important;
      margin-left: -130px!important;
    }
  }
  .bgImg{
    position:fixed;
    height:100%;
    width:100%;
    background: #fff;
    z-index:-1;
  }
  .bgImg>img{
    width: 100%;
    filter:blur(15px);
    -webkit-filter: blur(15px); 
    -moz-filter: blur(15px);
    -ms-filter: blur(15px);
  }
  .bgImg .mu-avatar{
    position: absolute;
    left: 50%;
    margin-left: -150px;
    top: 30px;
  }
  .controlBar{
    position: fixed;
    width: 100%;
    height: 180px;
    background: #fff;
    bottom: 0;
    z-index: 11;
    text-align:center;
  }
  
  .mu-slider{
    width: 70%!important;
    display: inline-block!important;
    margin-bottom: -7px!important;
  }
  
  
  .slideTime{
    width: 29px;
    display: inline-block;
  }
  .mu-content-block{
    font-size: 18px;
    color: #777
  }
  .mu-slider{
    display: inline-block;
    margin:0 3px -7px;
    width: 70%;
  }
</style>

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;
    },
  1. 如代碼所示,我在頂部導航添加了一個icon button,樣式來自Muse-ui綁定了一個點擊事件backpage,點擊後會回到上一個路由頁面。這個須要配合以前的高亮底部導航icon,才能實現返回上一路由的同時高亮相對應的icon。
  2. 還要注意的是,computed裏有兩個方法,第一個是獲取vuex裏面的當前曲目信息;第二個則是獲取進度條的百分比信息,這個方法實現了數據的雙向綁定,隨着後臺設定的計時器,不斷地更新,從而實現播放時進度條的變化。一樣,這裏的樣式也是來自Muse-uiSlider
  3. 這裏有一個須要注意的坑是,Muse-ui自帶了許多的函數,第一次寫的時候沒有注意,在進度條上綁定了一個mouseup事件,結果無效,後來才發現,其實已經自帶了change事件,還能夠實現移動端的兼容。因此寫代碼的時候必定要多看看官網文檔。
  4. 關於store.js裏的方法,play是播放/暫停,具體會根據當前音頻文件的paused(便是否暫停)來判斷。總的原理是首先獲取音頻的持續時間,而後經過一個定時器,不斷更新顯示時間,播放完成時,計時器中止。
  5. 計時器很關鍵,進度條和顯示時間的更新都須要它。可是計時器有個坑,若是把計時器聲明放在play方法裏,則沒法在audioEnd方法裏中止計時器,因此這裏咱們須要在最外層先聲明一個ctime,而後再在play方法裏把定時器賦值給ctime,這樣咱們就能夠隨時中止計時器了。
  6. audioEnd方法是播放中止時要作的事情,咱們會把中止按鈕切換成播放,把顯示時間修改掉,別忘了中止計時器。
  7. editProgress方法是點擊或拖動進度條時作的事情,咱們會改變當前音頻的currentTime,即當前時間,若是音頻是暫停狀態,咱們要讓它繼續播放。

4.search.vue

這也是一個比較核心的一個功能,畢竟推薦的歌單隻有幾個。看代碼:

<template>
  <div class="search">

    <!-- navbar here -->
    <mu-appbar>
      <mu-icon-button icon="navigate_before" slot="left" v-on:click="backpage"/>
      <div class="logo searchLogo">
        iPlayer
      </div>
      <mu-text-field icon="search" class="appbar-search-field"  slot="right" hintText="想聽什麼歌?" v-model="searchKey"/>
      <mu-flat-button color="white" label="搜索" slot="right" @click="getSearch(searchKey)"/>
    </mu-appbar>

    <!-- banner here-->

    
    <mu-list>       
      <template v-for="(item,index) in result.songs">
        <mu-list-item  :title="item.name" @click="getSong(item.id,item.name,item.artists[0].name,item.album.name,item.artists[0].id)">
          <mu-avatar slot="leftAvatar" backgroundColor="#fff" color="#bdbdbd">{{index+1}}</mu-avatar>
          <span slot="describe">
            <span style="color: rgba(0, 0, 0, .87)">{{item.artists[0].name}} -</span> {{item.album.name}}
          </span>
        </mu-list-item>
        <mu-divider/>
      </template>
    </mu-list>
    

    <div class="footer-rights">
      <h4>版權歸Godown Huang全部,請<a href="https://github.com/WE2008311">聯繫我</a>。</h4>
    </div>


  </div>
</template>

<script>


export default {
  
  name: 'search',
  data(){
    return{
      searchKey:''
    }
  },
  computed:{
    result(){
      return this.$store.getters.result;
    }
  },
  
  components: {
    
  },
  
  methods:{
    backpage(){
      window.history.go(-1);
    },
    getSearch(value){
      this.$store.commit('getSearch',value);
    },
    getSong(id,name,singer,album,arid){
      
      
      this.$store.commit('getSong',{id,name,singer,album,arid});
      this.$store.commit('play');
    }
    
  }
}
</script>
  

<style lang="less">
  @media screen and (max-width: 525px){
    .searchLogo{
      display: none;
    }
    .appbar-search-field{
      width: 200px!important;
    }
  }
  
  .appbar-search-field {
    color: #FFF;
    margin-top: 10px;
    margin-bottom: 0;
    &.focus-state {
      color: #FFF;
    }
    .mu-icon {
      color: #FFF;
    }
    .mu-text-field-hint {
      color: fade(#FFF, 54%);
    }
    .mu-text-field-input {
      color: #FFF;
    }
    
    .mu-text-field-focus-line {
      background-color: #FFF;
    }
  }

  .footer-rights>h4{
    color: #e1e1e1;
    font-weight: 100;
    font-size:.056rem;
    height:90px;
    padding-top: 10px;
    text-align: center;
  }

</style>

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下載安裝不用多說了。

解釋一下這個組件的兩個方法:

  1. getSearch是獲取搜索結果,它被綁定再搜索按鈕上,初始頁面是空白,經過傳遞關鍵字,用axios從api獲取搜索結果,再把結果顯示在頁面上。
  2. getSong綁定在每個搜索的結果上,有兩個步驟,第一是getSong,會把點擊的歌曲設置爲要播放的歌曲,並把相關信息傳遞給play.vue,讓它顯示在相應的地方;第二個步驟,會播放歌曲,也就是上面的play方法,具體沒必要再說。
  3. 這裏有一個坑,咱們可能須要經過vuex傳遞參數,可是有時候傳遞多個參數會出現undefined的狀況,這時候咱們要把參數們寫成{參數一,參數二,參數三}的形式。

5.songList

這個組件主要是歌單詳情頁,基本的樣式和搜索頁同樣,就是獲取歌單的內容不一樣,搜索頁面的列表是根據關鍵詞獲取的,歌單詳情頁的列表是根據歌單id獲取的,獲取的方式都是經過axios。

<template>
  <div class="songsList">

    <!-- navbar here -->
    <mu-appbar>
      <mu-icon-button icon="navigate_before" slot="left" v-on:click="backpage"/>
      <div class="logo">
        iPlayer
      </div>
    </mu-appbar>

    <!-- banner here-->

    <div class="listBgImg">
      <img :src="playlist.coverImgUrl" />
      <!-- 封面CD -->
      <mu-avatar  slot="left" :size="120" :src="playlist.coverImgUrl"/>
      
    </div>
    
    <mu-list>       
      <mu-sub-header>{{playlist.name}}</mu-sub-header>
      <template v-for="(item,index) in playlist.tracks">
        <mu-list-item  :title="item.name" @click="getSong(item.id,item.name,item.ar[0].name,item.al.name,item.ar[0].id)">
          <mu-avatar :src="item.al.picUrl" slot="leftAvatar"/>
          <span slot="describe">
            <span style="color: rgba(0, 0, 0, .87)">{{item.ar[0].name}} -</span> {{item.al.name}}
          </span>
        </mu-list-item>
        <mu-divider/>
      </template>
    </mu-list>
    
    <div class="footer-rights">
      <h4>版權歸Godown Huang全部,請<a href="https://github.com/WE2008311">聯繫我</a>。</h4>
    </div>



  </div>
</template>

<script>


export default {
  
  name: 'songsList',
  data(){
    return{

    }
  },
  components: {
    
  },
  computed:{
      playlist(){
        return this.$store.getters.playlist;
      }
      
  },
  methods:{
    backpage(){
      window.history.go(-1);
    },
    getSong(id,name,singer,album,arid){
      this.$store.commit('getSong',{id,name,singer,album,arid});
      this.$store.commit('play');
      
    }
  }
}
</script>
  

<style lang="css">
    
  .listBgImg{
    height:200px;
    width:100%;
    background: #fff;
    overflow: hidden;
  }
  .listBgImg>img{
    width: 100%;
    filter:blur(30px);
    -webkit-filter: blur(30px); 
    -moz-filter: blur(30px);
    -ms-filter: blur(30px);
  }
  .listBgImg .mu-avatar{
    position: absolute;
    left: 50%;
    margin-left: -60px;
    top: 130px;
  }
  .mu-list .mu-sub-header{
    /* position: absolute; */
    top: 260px;
    font-size: 16px;
    /* text-align: center; */
  }
  
  

</style>

沒什麼須要解釋的,注意咱們在getSong裏面傳遞的多個參數。

6.playerBox.vue

<template>
  <div class="playerBox">
      
      <audio ref="myAudio" :src="audio.location" @ended="audioEnd" id="playerBar"></audio>

      <div class="controlBarBtn" v-show="judgement()">
        
          <mu-icon-button icon="skip_previous"/>
          <mu-icon-button class="addPlus" icon="play_arrow" @click="play"/>
          <mu-icon-button icon="skip_next"/>
      </div>
  </div>
  
</template>

<script>



export default {



  name: 'playerBox',
  data(){
    
    return{
      
    }
  },
  components: {
    
  },

  computed:{
    audio(){
      return this.$store.getters.audio;
    }
  },
  methods:{
    play(){
      this.$store.commit('play');
    },
    audioEnd(event){
      this.$store.commit('audioEnd',event);
    },
    judgement(){
      let path=this.$route.path;
      if(path=="/play"){
        return true;
      }else{
        return false;
      }
    }
  }
  
}
</script>
  

<style lang="less" >
  .controlBarBtn{
    position: absolute;
    z-index:12;
    width: 243px;
    margin-left: -121.5px;
    
    top: 83%;
    left: 50%;
  }

  .controlBarBtn i.mu-icon{
    font-size: 36px;
    color: #03a9f4;
    left: 50%;
    margin-left: -18px;
    position: absolute;
    top: 10%;
  }
  
  .controlBarBtn .addPlus{
    top: 16px;
    width: 80px!important;
    height: 80px!important;
    margin: 0 30px!important;
  }
  .controlBarBtn .addPlus i.mu-icon{
    font-size: 60px;
    margin-left: -30px;
    top: 10%;
  }
</style>

這個頁面比較簡單,播放器audio標籤,綁定了ended事件,即播放完成後執行。
這裏有一個坑,解釋一下:我把播放器按鈕放在這裏了,爲何呢?以前我是放在play.vue裏的,可是我發現一個問題,就是經過點擊歌單的歌曲播放時,沒法改變播放/暫停按鈕,爲何呢?由於我改變按鈕的方法是用innerHTML改變,我爲何要用這種方法呢?由於Muse-ui的icon通過渲染,是以標籤的值的形式出現的。這就不得不獲取DOM了,可是若是把按鈕寫在play.vue裏,在歌單頁面時是獲取不到指定DOM的,由於當前頁面根本沒有這個DOM!只有把按鈕寫在在主組件裏的playerBox.vue裏,才能獲取到指定DOM。

可是寫在playBox.vue裏又有一個問題,按鈕會出如今每個頁面裏,可是咱們只要它出如今播放頁面就行了,因此咱們在這裏要給按鈕綁定一個v-show,裏面的內容就是判斷是否是在指定路由,若是是播放頁面,就顯示按鈕,不是,就隱藏按鈕。

axios和網易雲api

axios具體的配置我都在上面講了,這裏介紹一款網易雲的api和使用方法。

文檔在此

介紹一下使用方法,進入git把它下下來,在命令行執行:

$ node app.js

在瀏覽器輸入地址:

localhost:3000

看到彈出的頁面就說明服務器啓動成功了。而後咱們能夠在文檔裏查到具體請求的數據,好比banner啊,歌單啊,搜索啊,都能請求。咱們看到前面寫的axios請求裏的地址,都是具體請求的地址。

這裏要注意的是,這個api默認的是沒有開啓跨域的,看app.js裏有一段被隱藏的代碼就是跨域的相關設置,解除隱藏便可。

bug和未實現功能

目前還存在一個比較大的bug,就是在歌單點擊播放時,點擊第一次由於沒辦法獲取個去的url,沒法播放,只有再點擊一次才能播放,這個bug暫時尚未時間解決,會盡快解決。

而後目前尚未實現的功能是播放列表,天然上一曲/下一曲按鈕也沒有用了,歌曲播放一遍也就中止了,這個功能不算難,抽空把它作出來。

參考資料

這個app參考了一些技術文章,給了我很大的啓發,附上連接。
用vue全家桶寫一個「以假亂真」的網易雲音樂
DIY 一個本身的音樂播放器 2.0 來襲

結語

這個app前先後後,磨磨蹭蹭作了兩個月,好歹總算是作完了。學習仍是得找項目來作,雖然這個項目還很簡陋,可是仍是get到不少知識點,對於個人提升仍是蠻大的。

這種項目不算難,寫過的人也多,因此百分之八十的問題都能百度出來,剩下的百分之二十,技術社區裏提個問基本可以解決。項目仍是得本身寫一遍,寫的過程當中才能發現問題,也才能想辦法找到解決辦法,事情老是會比你想象的要簡單一點。

項目不算大,但要一步步寫下來總有可能有所遺漏,這裏是個人GitHub,你們能夠對照着看看有沒有遺漏。若是你喜歡個人項目,也但願star或者fork一波~

相關文章
相關標籤/搜索