Vue3+node.js网易云音乐实战项目(七)

底部播放按钮和播放功能的实现

其他页面可以看我页面专栏 Vue3实战项目-网易云APP 。
大家觉得有些地方可以写的更好写法可以给我留言私信,我会去修改
如果文章对你有帮助请点一个赞或收藏,谢谢

1、底部播放组件

接下来我们完成播放的组件

image-20220629095018575

首先,我们看手机上的网易云可以知道它是一个全局的组件,所以我们就在App.vue中注册一个。

image-20220629095146588

然后我们在component中创建playController.vue组件,把基础的结构填好,还是分为左右两边。

<template>
<!-- 播放 -->
  <div class="playController">
      <!-- 图片和名字部分 -->
      <div class="left">
          <img src="" alt="">
          <div class="content">
              <div class="title"></div>
          </div>
      </div>
      <!-- 播放按钮和列表按钮 -->
      <div class="right">
            <svg class="icon" aria-hidden="true">
                <use xlink:href="#icon-bofang"></use>
            </svg>
            <svg class="icon" aria-hidden="true">
                <use xlink:href="#icon-24gf-playlistHeart"></use>
            </svg>
      </div>
  </div>
</template>

image-20220629095819889

左边没有数据,所以就没显示,然后我们来弄数据,首先它是个全局组件,在每个页面都有显示,所以数据是全局的,我们在store下传递值(这里是利用的vuex)如果最开始创建项目时没选择安装需要安装一下

image-20220629095958590

然后我们在 state 中创建两个对象,播放列表和播放列表的索引,然后通过mutations 进行更新

  state: {
    // 播放列表
    playlist:[{al:{}}],
    // 当前播放的索引值
    palyCurrentIndex:0,
  },
  getters: {
  },
  mutations: {
    // 更新playlist
    setPlaylist(state,value){
      state.playlist = value
    }
  },

image-20220629100435136

这里设置一个 al:{} 是防止初始化的时候报错

然后我们需要传递参数,我们知道播放列表中都是来自于歌单里面的歌,所以我们找到ListView.vue 详细歌单页面来进行参数传递

import {useStore} from 'vuex'

//setup中
 const store = useStore()
 //setup中的onMounted中
 store.commit('setPlaylist',state.playlist.tracks)

image-20220629100836722

image-20220629100856693

然后我们在 playController.vue 中使用state中的值,我们可以看看是否拿的到值

<script>
import {useStore} from 'vuex'
export default {
    setup(){
        const store  = useStore()
        console.log(store);
        return{
            store
        }
    }
}
</script>

image-20220629101157950

这里我们可以看到是拿到了,我们在模板上渲染,我们还需要看一下传过来的值如何取,我们需要取得是一个歌的图片,歌名,作者

image-20220629101343342

这个我们之前在渲染歌单列表的时候使用过所以就是下面这几个

image-20220629102509027

​ 然后我就发现了,有些歌是两个人合作的或者多个人合作的,我渲染列表的时候没注意到,只取了第一个,所以要做一下修改。在 palylistview.vue 下,循环渲染一下。

                      <div class="anthor" >
                        <span v-for="(itemName,j) in item.ar" :key = "j">
                            <!-- 如果是最后一个就不加反斜杠 -->
                            <span v-if="j==item.ar.length-1">{{itemName.name}}</span>
                            <span v-else>{{itemName.name}}/</span>
                        </span>
                        <span class="album"> - {{item.al.name}}</span>
                      </div>

image-20220629151248729

image-20220629151325460

然后这里又出了一个新BUG,右边的图标被顶起来了,很难受。然后我们找到这个的样式,然后我调整了一下图标的大小

        .right{
            display: flex;    
            justify-content:center;
            align-items:Center; 
            .icon{
                width: 0.4rem;
                height: 0.4rem;
                opacity: 0.5;
                margin-left: 15px;
            }
        }

image-20220629164731013

回归正题,我们这里也需要循环渲染一下,这里我找了一个多人合作的歌

image-20220629151701807

可以看到效果可以直接拿 palylistview.vue 的过来用。

      <!-- 图片和名字部分 -->
      <div class="left">
          <img class="playImg" :src="$store.state.playlist[$store.state.palyCurrentIndex].al.picUrl" alt="">
          <div class="content">
              <div class="title">
                 <span>{{$store.state.playlist[$store.state.palyCurrentIndex].al.name}} -&nbsp;</span> 
              </div>
              <div class="anthor">
                <span v-for="(item,i) in $store.state.playlist[$store.state.palyCurrentIndex].ar " :key="i">
                    <!-- 如果是最后一个就不加反斜杠 -->
                    <span v-if="i==$store.state.playlist[$store.state.palyCurrentIndex].ar.length-1">{{item.name}}</span>
                    <span v-else>{{item.name}}/</span>
                </span>
            </div>
          </div>
      </div>

image-20220629153751438

image-20220629152704616

然后开始设置样式,还是从左到右,从图片开始先给图片取个名字,给它设置个大小,现在太大影响页面。

<img class="playImg" :src="$store.state.playlist[$store.state.palyCurrentIndex].al.picUrl" alt="">

image-20220629153405648

        .playImg{
            width: 1rem;
            height: 1rem;
        }

先利用flex把所有的都变成横排列,并把它固定到页面底部

.playController{
    width: 7.5rem;
    padding: 0 0.4rem;
    display: flex;
    position: fixed;
    left: 0;
    bottom: 0;
    justify-content: space-between;
    // 左边部分
    .left{
        display: flex;
        // 图片
        .playImg{
            width: 1rem;
            height: 1rem;
        }
        // 内容
        .content{
            display: flex;
        }
    }
}

image-20220629153909856

然后,调整图片是个圆形而且有外边框

        .playImg{
            width: 0.8rem;
            height: 0.8rem;
            border-radius: 100%;
            border: 5px solid black;
        }

image-20220629154510888

然后调整字体,居中对齐,歌手名字变小。调整盒子间距

        // 内容
        .content{
            display: flex;
            padding-left: 10px;
            align-items: center;
            width: 4rem;
            font-size: 14px;
            display: -webkit-box;
            -webkit-line-clamp: 1;
            -webkit-box-orient: vertical;
            overflow: hidden;
            .anthor{
                font-size: 10px;
                opacity: 0.5;
            }
        }

image-20220629160123091

然后调整右边图标的大小、边距和居中

    .right{
        display: flex;    
        justify-content:center;
        align-items:Center;    
        .icon{
            width: 0.5rem;
            height: 0.5rem;
            margin-left: 15px;
        }
    }

image-20220629163418677

然后在微调一下,给整体一个固定的高度,让它看起来没那么挤

.playController{
    width: 7.5rem;
    height: 1rem;
    padding: 0 0.4rem;
    display: flex;
    justify-content: space-between;
}

然后这里给了一个固定的宽高后,也要让图片据中

    .left{
        display: flex;    
        justify-content:center;
        align-items:Center;
}

image-20220629164122629

这样看起来就好多了。

然后这里又有一个小BUG,就是在启动项目或者刷新页面的时候,第一次是没有值的,就像这样。

image-20220629165528403

然后,我考虑了两个办法

  1. 利用v-if

    在开始的时候不显示这个播放,也就是在playController最外层的div上加

    <div class="playController" v-if="$store.state.playlist[$store.state.palyCurrentIndex].al.picUrl">
    </div> 
    

    image-20220629170609440

    效果大概就是下面这样

    354624242321211

  2. 添加一个初始的数据

    在state上添加一个初始的数据上去

      state: {
        // 播放列表
        playlist:[{
          al:{
            name:"苦茶子",
            picUrl:"http://p4.music.126.net/x8LI4oQGho-afSQFthr7wg==/109951167084000790.jpg",
          },
          ar:[{
            name:"Starling8"
          }]
      }],
    

    然后开始渲染的时候就会渲染自己自定义的数据,大家可以选一首自己喜欢的歌放上去

    image-20220629172133156

2、音乐播放与暂停

然后我们来实现获取音乐的效果,先看一下接口说明

image-20220629172332032

我们可以看到,需要将audio 的src改成以下形式

<audio controls :src="https://music.163.com/song/media/outer/url?id=id.mp3"></audio>

那么id我们可以拿到,也就是下面这些,所以只需要稍微修改一下

image-20220629201002338

    <audio controls :src="`https://music.163.com/song/media/outer/url?id=${$store.state.playlist[$store.state.palyCurrentIndex].id}.mp3`"></audio>

这里我设置成 controls 是为了让播放按钮显示,用来测试一下,大家也可以试试,毕竟写了那么久,就为了听一下。

image-20220629201140573

然后这里我们就需要修改一下我们在 state上设置的默认值了

  state: {
    // 播放列表
    playlist:[{
      al:{
        id: 73828956,
        name: "年少",
        pic: 109951163901435150,
        picUrl: "http://p3.music.126.net/dHRFJJ9nfF_9YTxOz9UXUQ==/109951163901435148.jpg",
        pic_str: "109951163901435148",
      },
      ar:[
        {
          id: 30284835,
          name: "枯木逢春"},
        ],
      id: 1316921651
  }],
    // 当前播放的索引值
    palyCurrentIndex:0,
  },

防止第一次渲染的时候没有数据,然后我们设置一个点击事件来触发audio,先利用ref获取audio元素

<!-- template -->
<svg class="icon" aria-hidden="true" @click="play">
       <use xlink:href="#icon-bofang"></use>
</svg>

<audio ref="audio" :src="`https://music.163.com/song/media/outer/url?id=${$store.state.playlist[$store.state.palyCurrentIndex].id}.mp3`"></audio>

image-20220630102819168

然后我们拿一下audio元素

import {ref} from 'vue'
const audio = ref(null)
function play(){
     audio.value.play()
}
return{
    store,
    audio,
    play
}

image-20220630103035126

然后我们就可以点击播放按钮听到音乐了

image-20220630103101501

然后我们可以输出一下 audio.value,看一看什么在控制播放

image-20220630103603262

我们可以看到就是它在改变控制开关,然后我们修改一下代码

        function play(){
            console.log(audio);
            // 控制音乐的开关
            if(audio.value.paused){
               audio.value.play()
            }else{
                audio.value.pause()
            }
        }

这样就能实现音乐的开和关了,然后我们给他添加一个图标让它看起来更好看一点,找一个暂停的图标,利用v-show来进行判断,判断的条件我们自己自定义一个

      <div class="right">
          <!-- 播放与暂停 -->
            <svg v-show="pause" class="icon" aria-hidden="true" @click="play">
                <use xlink:href="#icon-bofang"></use>
            </svg>
            <svg v-show="!pause" class="icon" aria-hidden="true" @click="play">
                <use xlink:href="#icon-zanting"></use>
            </svg>
            <svg class="icon" aria-hidden="true">
                <use xlink:href="#icon-24gf-playlistHeart"></use>
            </svg>
      </div>
// 控制图片切换
let pause = ref(true)
function play(){
            // console.log(audio);
            // 控制音乐的开关
            if(audio.value.paused){
               audio.value.play()
               pause.value = false
            }else{
                audio.value.pause()
                pause.value = true
      }
}
return{
     store,
     audio,
     pause,
     play
}

3546241242321211

3、切换歌曲

然后接下来我们就来做切换歌曲,点击一下歌单列表达到换歌的效果,这里首先又排除一个BUG。

image-20220630152841443

这些歌左边的播放按钮,其实是MV,有些歌是没有MV的

image-20220630152923276

然后我在这个图标这里用v-if做了一个判断,如果它等于0就不再渲染它,修改的代码为playlistView.vue这个页面

                <svg v-if="item.mv!=0"  class="icon" aria-hidden="true">
                    <use xlink:href="#icon-bofang"></use>
                </svg>

image-20220630153116232

image-20220630153121819

然后修复的时候,又发现一个小问题,就是歌名渲染错误了

image-20220630185201315

然后我看了一下,应该是直接.name

image-20220630185233239

image-20220630185213908

              <div class="title">
                 <span>{{$store.state.playlist[$store.state.palyCurrentIndex].name}} -&nbsp;</span> 
              </div>

image-20220630185324601

这样就行了,然后回归正题,我们来做点击切换音乐的效果,首先我们要知道数据是从state 中来的,所以我们需要操作 state

image-20220630163356363

然后我们再来看代码,是用id来表达每个歌曲的,所以我们在mutations只需要接收过来的值替换现有的id就可以实现效果。

    // 切换歌曲
    setPlayIndex(state,value){
      state.palyCurrentIndex = value
    }

image-20220630163525583

然后在playlistView中我们需要点击的是一整块列表的div

image-20220630163636409

我们给它绑定一个click事件,传入它是列表的第几个

image-20220630163744948

然后我们开始写点击事件

import {useStore} from 'vuex'
export default {
     props:['playlist'],
     setup(){
        const store  = useStore()
        // 切换歌曲
        function setIndex(index){
            store.commit('setPlayIndex',index)
        } 
        return {
            store,
            setIndex
        }
     }
}

这样我们的点击切换歌曲就写好了

35462742321211

然后我们看到这个图标是没有变化的,并且没有自动播放,这里我在state里面加上了这两个状态,肯定有其他方法,但我一直没实现,希望大家指点我一下。

这里修改的地方分别的 store的index.js

//  state新增下面这两行数据
// 播放图片的显示和隐藏
    pause:true,
    // 歌曲是否自动播放
    autoplay:false,

//mutations 加了一个方法,由于都是是否控制所以只传一个值取反
setAutoPlay(state,value){
      state.pause = !value
      state.autoplay = value
}       

image-20220630184103312

然后修改的地方 playController.vue

      <!-- 播放按钮和列表按钮 -->
      <div class="right">
          <!-- 播放与暂停 -->
            <svg v-show="$store.state.pause" class="icon" aria-hidden="true" @click="play">
                <use xlink:href="#icon-bofang"></use>
            </svg>
            <svg v-show="!$store.state.pause" class="icon" aria-hidden="true" @click="play">
                <use xlink:href="#icon-zanting"></use>
            </svg>
            <svg class="icon" aria-hidden="true">
                <use xlink:href="#icon-24gf-playlistHeart"></use>
            </svg>
      </div>
      <!-- 获取音乐播放 -->
    <audio :autoplay="$store.state.autoplay" ref="audio" :src="`https://music.163.com/song/media/outer/url?id=${$store.state.playlist[$store.state.palyCurrentIndex].id}.mp3`"></audio>

image-20220630184233725

        // 控制图片切换
        function play(){
            // console.log(audio);
            // 控制音乐的开关
            if(audio.value.paused){
               audio.value.play()
               store.state.pause = false
            }else{
                audio.value.pause()
                store.state.pause = true
            }
        }

image-20220630184255658

然后就是playlistView.vue文件

		const  state = true
	// 切换歌曲
        function setIndex(index){
            store.commit('setPlayIndex',index)
            store.commit('setAutoPlay',state)
        } 

image-20220630184403478

这样大部分就完成了,然后会有一个小问题,就是从歌单点了歌之后返回另一个歌单,会自动播放新歌单的第一首歌。然后我又在recommendMusic.vue给了一个click事件

        <router-link :to="{path:'/listview',query:{id:img.id}}" class="imgs" v-for="(img,index) in songlistResult.list" :key="index" @click="autoplay">
            <img :src="img.picUrl"/>
            <div class="count">
                <span></span>
                <!--播放量  -->
                <span>{{formatNum(img.playcount)}}</span>
            </div>
            <p>{{img.name}}</p>
         </router-link>

image-20220630184618232

import {useStore} from 'vuex'     
const store  = useStore()
const state = false
function autoplay(){
   store.commit('setAutoPlay',state)
}
return {
    autoplay
}

image-20220630184727995

最后的效果

35460742321211

  • 31
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

周粥粥ya

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值