• 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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    image-20220629095819889

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

    image-20220629095958590

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

      state: {
        // 播放列表
        playlist:[{al:{}}],
        // 当前播放的索引值
        palyCurrentIndex:0,
      },
      getters: {
      },
      mutations: {
        // 更新playlist
        setPlaylist(state,value){
          state.playlist = value
        }
      },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    image-20220629100435136

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

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

    import {useStore} from 'vuex'
    
    //setup中
     const store = useStore()
     //setup中的onMounted中
     store.commit('setPlaylist',state.playlist.tracks)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    image-20220629100836722

    image-20220629100856693

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

    <script>
    import {useStore} from 'vuex'
    export default {
        setup(){
            const store  = useStore()
            console.log(store);
            return{
                store
            }
        }
    }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    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;
                }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    image-20220629153751438

    image-20220629152704616

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

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

    image-20220629153405648

            .playImg{
                width: 1rem;
                height: 1rem;
            }
    
    • 1
    • 2
    • 3
    • 4

    先利用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;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    image-20220629153909856

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

            .playImg{
                width: 0.8rem;
                height: 0.8rem;
                border-radius: 100%;
                border: 5px solid black;
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    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;
                }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    image-20220629160123091

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

        .right{
            display: flex;    
            justify-content:center;
            align-items:Center;    
            .icon{
                width: 0.5rem;
                height: 0.5rem;
                margin-left: 15px;
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    image-20220629163418677

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

    .playController{
        width: 7.5rem;
        height: 1rem;
        padding: 0 0.4rem;
        display: flex;
        justify-content: space-between;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

        .left{
            display: flex;    
            justify-content:center;
            align-items:Center;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    image-20220629164122629

    这样看起来就好多了。

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

    image-20220629165528403

    然后,我考虑了两个办法

    1. 利用v-if

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

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

      image-20220629170609440

      效果大概就是下面这样

      354624242321211

    2. 添加一个初始的数据

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

        state: {
          // 播放列表
          playlist:[{
            al:{
              name:"苦茶子",
              picUrl:"http://p4.music.126.net/x8LI4oQGho-afSQFthr7wg==/109951167084000790.jpg",
            },
            ar:[{
              name:"Starling8"
            }]
        }],
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11

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

      image-20220629172133156

    2、音乐播放与暂停

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

    image-20220629172332032

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

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

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

    image-20220629201002338

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

    这里我设置成 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,
      },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    防止第一次渲染的时候没有数据,然后我们设置一个点击事件来触发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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    image-20220630102819168

    然后我们拿一下audio元素

    import {ref} from 'vue'
    const audio = ref(null)
    function play(){
         audio.value.play()
    }
    return{
        store,
        audio,
        play
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    image-20220630103035126

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

    image-20220630103101501

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

    image-20220630103603262

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

            function play(){
                console.log(audio);
                // 控制音乐的开关
                if(audio.value.paused){
                   audio.value.play()
                }else{
                    audio.value.pause()
                }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这样就能实现音乐的开和关了,然后我们给他添加一个图标让它看起来更好看一点,找一个暂停的图标,利用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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    // 控制图片切换
    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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    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>
    
    • 1
    • 2
    • 3

    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>
    
    • 1
    • 2
    • 3

    image-20220630185324601

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

    image-20220630163356363

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

        // 切换歌曲
        setPlayIndex(state,value){
          state.palyCurrentIndex = value
        }
    
    • 1
    • 2
    • 3
    • 4

    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
            }
         }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

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

    35462742321211

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

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

    //  state新增下面这两行数据
    // 播放图片的显示和隐藏
        pause:true,
        // 歌曲是否自动播放
        autoplay:false,
    
    //mutations 加了一个方法,由于都是是否控制所以只传一个值取反
    setAutoPlay(state,value){
          state.pause = !value
          state.autoplay = value
    }       
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    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
                }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    image-20220630184255658

    然后就是playlistView.vue文件

    		const  state = true
    	// 切换歌曲
            function setIndex(index){
                store.commit('setPlayIndex',index)
                store.commit('setAutoPlay',state)
            } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    image-20220630184618232

    import {useStore} from 'vuex'     
    const store  = useStore()
    const state = false
    function autoplay(){
       store.commit('setAutoPlay',state)
    }
    return {
        autoplay
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    image-20220630184727995

    最后的效果

    35460742321211

  • 相关阅读:
    【英语:基础进阶】D2.短句子关键词训练
    【定时同步系列9】QPSK基带调制+Gardener定时误差检测+解调误码率曲线之MATLAB并行仿真姊妹篇二
    LT498. 对角线遍历
    试用 ModVB(一):安装教程+使用 JSON 常量和 JSON 模式匹配
    fiddler的使用
    Tomcat web.xml文件中的mime-mapping
    SpringMVC之综合案例
    实时翻译软件-大家都在用的实时免费翻译软件
    Spring5复习笔记
    WordPress建站教程:10步快速搭建个人网站
  • 原文地址:https://blog.csdn.net/NITIQ/article/details/125546450