网易云音乐播放器项目实现步骤部分(2)

创建歌单详情页面,然后在页面中分别以组件形式引入:

分为导航栏,歌单详情,歌单列表

在views里面创建listView.vue组件在app.vue里面引入

通过HomeView组件中的musicList组件中的router-link标签跳转并将歌单id传参

  <router-link
           :to="{ path: '/listView', query: { id: item.id } }"
           class="swiper-slide"
           v-for="item in musics.musicList"
           :key="item.id"
         >
           <img :src="item.picUrl" alt="" />
           <div class="name">{{ item.name }}</div>
           <div class="count">
             <svg class="icon" aria-hidden="true">
               <use xlink:href="#icon-bofang"></use>
             </svg>
             <span>{{ changeValue(item.playCount) }}</span>
           </div>
         </router-link>

在listView组件里面创建listViewTop.vueplayList.vue组件

在components里面创建listViewTop.vueplayList.vue

 

 

在api/index.js文件中创建访问api的函数并抛出

//获取歌单详情的api uid:当前歌单的id
export function getPlaylistDetail(id){
   return axios.get(`${baseUrl}/playlist/detail?id=${id}`)
}

在listView组件中使用getPlaylistDetail方法通过id获取歌单详情数据,调用store中的setPlaylist函数实现将歌单中的歌单列表数据传递到store中的playlist数据

setup( )

  setup() {
   const route = useRoute(); //当前组件的路由信息对象
   let musics = reactive({ //保存的是当前歌单的详情
     playlist: {
       creator: { }, //歌单详情
       tracks: { }, //播放列表
    },
  });
   onMounted(async () => {
     let id = route.query.id;
     console.log(id)
     let res = await getPlaylistDetail(id);
     musics.playlist = res.data.playlist
     console.log(res.data)
       //进入歌单详情页面,将歌单中的歌单列表数据传递到store的playlist中
     //commit触发store对象中的mutations中的函数
     store.commit("setPlaylist", musics.playlist.tracks);
       //将歌单下标重置为0,实现进入歌单详情选择第一首歌曲
     store.commit("setPlayCurrentIndex", 0);
  });
   return { musics };
},

然后使用props方法将数据传给子组件listViewTop.vueplayList.vue组件使用

传参

 <div class="listView">
     <!-- 歌单详情 -->
     <listViewTop :playlist="musics.playlist"/>
     <!-- 歌曲列表 -->
     <playList :playlist="musics.playlist"/>
 </div>

接收数据

export default {
 name: "playList",
 data() {
   return {};
},
 props: ["playlist"]
}

在使用html和css搭建listViewTop.vueplayList.vue组件的页面样式

歌曲播放器

在components创建playController.vue组件在App.vue中引入固定在页面最下方

store中引入歌曲的相关数据

import { mapState } from "vuex";

export default {
 name: "playController",
 computed: {
   ...mapState(["playlist", "playCurrentIndex", "paused"]),
}
}

在视图( 网页 )中展示数据

<div class="playController">
    <div class="left" @click="show=!show">
     <img :src="playlist[playCurrentIndex].al.picUrl" alt=""  />
     <div class="content">
       <div class="title">{{ playlist[playCurrentIndex].name }}</div>
       <div class="tips">横滑可以切换上下首哦</div>
     </div>
   </div>
   <div class="right">
     <svg v-if="paused" class="icon" aria-hidden="true" @click="playMusic">
       <use xlink:href="#icon-bofang"></use>
     </svg>
     <svg v-else class="icon" aria-hidden="true" @click="playMusic">
       <use xlink:href="#icon-zanting"></use>
     </svg>
     <svg class="icon" aria-hidden="true">
       <use xlink:href="#icon-list"></use>
     </svg>
   </div>
</div>

效果如下:

 

 

实现歌曲播放

html标签

<!-- audio的常用属性与方法 -->
   <!-- autoplay属性 : 自动播放 -->
   <!-- paused:当前歌曲的暂停状态 -->
   <!-- play() 播放当前歌曲 -->
   <!-- pause()暂停歌曲 -->
   <audio ref="audio"
     :src="`https://music.163.com/song/media/outer/urlid=${playlist[playCurrentIndex].id}.mp3`">
</audio>
<!-- src单向绑定   歌曲id -->    

控制歌曲播放与暂停功能

playMusic这个函数与播放(暂停)图标绑定,@click点击事件触发

 methods: {
   //音乐点击播放与暂停按钮时触发的函数
   playMusic() {
     // this.@refs.audio 指的是当前的audio标签
     if (this.$refs.audio.paused) {
       //处于暂停状态
       store.commit("setPlayAudio", false); //改变图标
       this.$refs.audio.play(); //音乐播放
    } else {
       store.commit("setPlayAudio", true); //改变图标
       this.$refs.audio.pause(); //音乐暂停
    }
  },
}

在切换歌单时需要重置播放按钮的状态paused(修复Bug: 用于修复切换歌单歌曲不播放而按钮是处于播放按钮状态而非暂停状态 )

首先在setup( )中获取store中的state中的数据

先在listView组件中先引入{ useStore }方法

import { useStore } from 'vuex';

在setup( )中使用

 let store = useStore() //当前组件的状态管理库( store )对象
//用于在setup()中获取store中的数据

进入不同歌单重置播放按钮状态( 用于修复切换歌单歌曲不播放而按钮是处于播放按钮状态而非暂停状态 )

 onMounted(async () => {  
     //进入歌单详情页面,判断进入的是不是当前播放歌曲的歌单,
    如果进入的歌单详情不是当前播放歌曲的歌单详情 歌曲暂停 播放按钮状态改变
    通过当前播放歌曲的id与进入歌单的第一首歌曲的id进行判断,来判断进入的是不是同一歌单
    因为同一个歌单的歌曲会继续播放所以需要if判断
     if(store.state.playlist[0].id != musics.playlist.tracks[0].id){
         //commit触发store对象中的mutations中的函数
       store.commit("setPlayAudio", true);
    }
  })

在切换同一个歌单列表中的歌曲时需要重置播放按钮的状态paused(修复Bug:用于修复切换歌曲时歌曲不播放而按钮处于播放状态的bug)

在切换歌曲事件中重置播放暂停按钮的状态( paused : true )

//
methods:{
   //index是切换的歌曲的下标
setPlayCurrentIndex(index) {
       通过当前播放歌曲的下标与切换到的歌曲的下标进行判断,来判断点击的是不是同一首歌曲
       为什么加if判断,因为点击同一首歌曲,歌曲不会暂停,但是按钮已经重置为暂停状态
    if (this.playCurrentIndex != index) {
      store.commit("setPlayAudio", true);
    }
    store.commit("setPlayCurrentIndex", index);
  }
}

点击事件触发以上的setPlayCurrentIndex( index )方法

        <svg click="setPlayCurrentIndex(index)" class="icon" aria-hidden="true">
           <use xlink:href="#icon-bofang3"></use>
        </svg>

歌曲详情页面

在components创建playMusic.vue组件在playController.vue中引入

定位到页面上覆盖网页其他内容

先引入store里的数据和playController.vue里的playMusic函数

import { mapState } from "vuex";
export default {
 name: "playMusic",
 data() {
   return {
     isPlaycontent: true, //控制唱片与歌词的切换
  };
},
   // playMusic 是控制歌曲播放与暂停的函数
 props: ["playMusic"],
 computed: { //歌单详情   //当前歌曲的下标 //播放的状态(播放与暂停) //歌词  
   ...mapState(["playlist", "playCurrentIndex", "paused","lyric"]),
},
};

 

使用html和css构建页面样式

实现唱片与歌词内容的切换

先在data( )添加一条数据用来控制唱片与歌词的切换

data() {
   return {
     isPlaycontent: true, //控制唱片与歌词的切换
  };

默认显示唱片,点击事件改变isPlaycontent用于切换唱片和歌词

   <!-- 显示唱片内容 -->
   <div
     class="playContent"
     v-if="isPlaycontent"
     @click="isPlaycontent = !isPlaycontent"
   >
  <!-- 显示歌词内容 -->
   <div class="playLyric" v-else @click="isPlaycontent = !isPlaycontent">

实现唱片的动画

通过修改class来实现动画的调用

通过paused来确定相关动画的class名是否存在

<div
     class="playContent"
     v-if="isPlaycontent"
     @click="isPlaycontent = !isPlaycontent"
   >
     <img
       class="needle"
       :class="{ active: !paused }"
       src="@/assets/needle-ip6.png"
       alt=""
     />
     <img class="disc" src="@/assets/disc-ip6.png" alt="" />
     <img
       class="playImg"
       :class="{active : !paused}"
       :src="playlist[playCurrentIndex].al.picUrl"
       alt=""
     />
   </div>
//杆子下降动画
.needle {
     width: 2.5rem;
     height: auto;
     position: absolute;
     left: 3.5rem;
     transform-origin: 0.3rem 0;
     transform: rotate(-10deg);
     transition: all 1s;
     z-index: 10;
  }
   .needle.active {
     width: 2.5rem;
     height: auto;
     position: absolute;
     left: 3.5rem;
     transform-origin: 0.3rem 0;
     transform: rotate(15deg);
     transition: all 1s;
     z-index: 10;
  }
//唱片旋转动画  
   .playImg.active {
     animation: fadenum 10s infinite linear;
     @keyframes fadenum{
  100%{transform:rotate(360deg);}
}

 

实现歌曲详情里的歌曲播放与暂停

单击事件调用从父级获取的playMusic方法来实现歌曲的播放与暂停

    <svg v-if="paused" class="icon play" aria-hidden="true" @click="playMusic" >
       <use xlink:href="#icon-bofang"></use>
   </svg>
   <svg v-else class="icon play" aria-hidden="true" @click="playMusic">
       <use xlink:href="#icon-zanting"></use>
   </svg>

playMusic函数详情

playMusic() {
     // this.@refs.audio 指的是当前的audio标签
     if (this.$refs.audio.paused) {
       //处于暂停状态
       store.commit("setPlayAudio", false);
       this.$refs.audio.play();
    } else {
       store.commit("setPlayAudio", true);
       this.$refs.audio.pause();
    }
  },

实现歌词的展示

在api/index.js中创建getLyric这个方法( 地址是查找网易云api文档获得)

//获取歌词的api id 当前歌曲的id /lyric?id=33894312
export function getLyric(id){
   return axios.get(`${baseUrl}/lyric?id=${id}`)
}

在store的actions里面创建一个方法调用getLyric(id)这个函数

 //acitons 定义修改store 的异步数据
 actions: {
     async reqLyric(content,payload){ //获取歌词详情的函数
       //payload.id 当前播放歌曲的id
       let result = await api.getLyric(payload.id)
       // console.log(result)
       content.commit('setLyric',result.data.lrc.lyric) // 修改歌词内容为当前获取的歌词数据
       //我们使用comit触发mutations中定义的函数(修改state中同步数据)
    }
},

再在playController组件(歌曲详情的父级)调用reqLyric这个函数

export default {
 components: { playMusic },
 name: "playController",
 data(){
   return{
     show:false
  }
},
 computed: {
   ...mapState(["playlist", "playCurrentIndex", "paused"]),
},
 mounted() {
   // 使用dispatch触发actions中定义的函数(修改state中异步数据)
   this.$store.dispatch('reqLyric',{id:this.playlist[this.playCurrentIndex].id})
},
 updated() {
   this.$store.dispatch('reqLyric',{id:this.playlist[this.playCurrentIndex].id})
},
};

在reqLyric里调用setLyric函数用于修改state里数据

  content.commit('setLyric',result.data.lrc.lyric) // 修改歌词内容为当前获取的歌词数据
       //我们使用comit触发mutations中定义的函数(修改state中同步数据)
state: {
   playlist: [ //音乐播放列表 ,默认有一条歌曲
    {
       name: "追梦赤子心",
       id: 355992,
       al: {
         id: 35139,
         name: "追梦痴子心",
         pic: 19061133579343590,
         picUrl: "http://p3.music.126.net/XDncptlBJ4_LN3hLBx-8aw==/19061133579343591.jpg",
         pic_str: "19061133579343591",
      }
    }
  ],
   playCurrentIndex: 0, //当前播放歌曲在播放列表中的下标
   paused : true,
   lyric : "" ,//当前播放歌曲的歌词

再在引入store里的state里的lyric( 歌词)数据

import { mapState } from "vuex";
export default {
 name: "playMusic",
 data() {
   return {
     isPlaycontent: true, //控制唱片与歌词的切换
  };
},
 props: ["playMusic"],
 computed: {
   ...mapState(["playlist", "playCurrentIndex", "paused","lyric"]),
},
};

简单展示歌词内容

<!-- 显示歌词内容 -->
   <div class="playLyric" v-else @click="isPlaycontent = !isPlaycontent">
    {{lyric}}
   </div>

效果展示:

 

 

 
posted @ 2022-05-11 15:10  Lcw零  阅读(514)  评论(0编辑  收藏  举报