目录

Vue音乐播放器

Vue音乐播放器

前言

本项目是基于vue3实现的一个音乐播放器,该项目是从网易云音乐接口获取相关音乐数据,再通过播放音乐的相关插件工具来实现给用户展示与播放功能。

主要功能展示

  • 首页功能展示

https://i-blog.csdnimg.cn/direct/8bcf5c9ab4894754999fd1c9223777e1.png

  • 实现音乐播放功能

https://i-blog.csdnimg.cn/direct/3958edb1e99d4147bbe16755121c07d1.png

  • 实现音乐视频播放功能

https://i-blog.csdnimg.cn/direct/002a8891e5734dd5b59d3daf53224c7b.png

  • 实现搜索功能

https://i-blog.csdnimg.cn/direct/20f2542f5b9346348a64e25168835140.png

主要功能代码实现

  • 首页代码

<template lang="html">
  <div class="css">
    <ul>
      <li>
        <router-link to="/home">
          首页
        </router-link>
      </li>
      <li>
        <router-link to="/search">
          搜索
        </router-link>
      </li>
    </ul>
    <router-view />
  </div>
</template>

<script>
export default {
}
</script>

<style scoped>

.css {
  background-color: #f8f6f6;
}


.css ul{
  display: flex;
  height: 50px;
  line-height: 50px;
  background: #efafaf;
}

.css ul li {
  flex: 1;
  text-align: center;
}

.css ul li a{
  font-size: 16px;
  color: #0e0b0b;
}

</style>
  • 音乐播放功能

<template lang="html">
  <div class="play">
    <div class="header">
     <div class="title">
       <router-link to="/">
         <i class="iconfont icon-shouye left"></i>
       </router-link>
       <div class="music">
          <p>{{this.name}}</p>
          <span
            class="author"
            v-for="(item,index) in this.author"
            :key="index"
            style="margin-right:2px"
          >{{item.name}}</span>
       </div>
       <router-link to="/search">
        <i class="iconfont icon-sousuo right"></i>
      </router-link>
     </div>
   </div>

   <div class="songs">
			<div class="songs-img">
        <img :src="this.songPic" :alt="this.name" />
			</div>
		</div> 
    <div class="songPlay">
      <audio ref="player" :src="this.songUrl" controls autoplay></audio>
		</div>
  </div>
</template>

<script>


import "../assets/font/iconfont.css"//音乐播放器样式引入

export default {
  name:"MusicPlay",
  props: {
    songid: {//接收传递的参数
      type: [String, Number],
      default: ""
    }
  },
  data() {
    return {//保存请求得到的参数
      songUrl:" ",
      songPic:"",
      name:"",
      author:[],
    };
  },  
  mounted(){//请求参数
    if(this.$route.params.songid){
       // 获取歌曲详情
    let songDetsil = this.HOST + `/song/detail?ids=${this.$route.params.songid}`;
    // 获取歌曲url地址
    let songUrls = this.HOST + `/song/url?id=${this.$route.params.songid}`;
    // 发起请求
    this.$axios.get(songDetsil).then(res => {
        this.songPic=res.data.songs[0].al.picUrl
        this.name=res.data.songs[0].name
        this.author=res.data.songs[0].ar
        this.$axios.get(songUrls).then(res => {
            this.songUrl= res.data.data[0].url;
          })
          .catch(err => console.log('获取歌曲音频报错', err.response.data.message));
      })
      .catch(err => {
        console.log('获取歌曲信息报错', err.response.data.message);
      });
    }
  },
}
</script>

<style scoped>

.play{
  background-color: #f4cccc;
}
.header{
	padding:15px;
  background-color: #efafaf;
}

.music{
	flex: 1;
	font-size: 20px;
}

.title{
	display: flex;
	text-align: center;
}

.left{
	font-size: 30px;
}

.right{
	font-size: 30px;
}

.songs{
	padding: 15px;
  margin-top: 30px;
}

.songs-img{
  width:345px;
  height:280px;
}



.songPlay{
	width: 100%;
  display: flex;
  margin-top: 40px;
}

.songPlay audio{
	width: 100%;
}


.author{
  font-size: 12px;
  color: #999;
}

</style>
  • 视频播放功能

<template lang="html">
    <div class="play">
      <div class="header">
        <div class="title">
       <router-link to="/">
         <i class="iconfont icon-shouye left"></i>
       </router-link>
       <div class="movie">
        <p>{{this.name}}</p>
       </div>
       <router-link to="/search">
        <i class="iconfont icon-sousuo right"></i>
      </router-link>
      <br>
     </div>
     </div>
     <Video
        :videoUrl="fileUrl"
        :coverUrl="coverUrl"
      ></Video>
     </div>
    
  </template>
  
  <script>
  
  
  import "../assets/font/iconfont.css"//样式
  import Video from "../components/Video.vue"

  
  export default {
    props: {
      mvid: {//接收参数
        type: [String, Number],
        default: ""
      }
    },
    components:{
        Video
    },
    data() {
    return {
      // 视频数据
      name:"",
      fileUrl: "",
      coverUrl: "",
    };
  },

    mounted(){
      if(this.$route.params.mvid){
         // 获取歌名、歌手、时长、id
      let mvCover = this.HOST + `/mv/detail?mvid=${this.$route.params.mvid}`;
      // 获取歌曲音频
      let mvUrl = this.HOST + `/mv/url?id=${this.$route.params.mvid}`;
      // 发起请求
      this.$axios.get(mvCover).then(res => {
        this.coverUrl=res.data.data.cover
        this.name=res.data.data.name
        console.log(this.coverUrl)
          this.$axios.get(mvUrl).then(res => {
            console.log(res.data.data)
            this.fileUrl=res.data.data.url
            })
            .catch(err => console.log('获取视频详情报错', err.response.data.message));
        })
        .catch(err => {
          console.log('获取视频url信息报错', err.response.data.message);
        });
      }
    }
}
  </script>
  
  <style scoped>
  
  .play{
    background-color: #f4cccc;
  }
  .header{
      padding:15px;
    background-color: #efafaf;
  }
  
  .movie{
      flex: 1;
      font-size: 20px;
  }
  
  .title{
      display: flex;
      text-align: center;
  }
  
  .left{
      font-size: 30px;
  }
  
  .right{
      font-size: 30px;
  }
  
  .songs{
      padding: 15px;
    margin-top: 40px;
  }
  
  </style>
  
  • 搜索功能

<template lang="html">
  <div class="search">
    <div class="title">
      <input type="text" name="" placeholder="请输入搜索内容" v-model="Content">
      <button  type="button" @click="search" name="button">搜索</button>
    </div>
    <ul class="list ">
     <router-link :key="index" tag="li" 
     :to="{name:'MusicPlay',
     params:{songid:item.id}}" 
     class="song" v-for="(item,index) in list">
       <div class="left">
         <div class="name ">
             <div>
                 <span>{{ item.name }}</span>
             </div>
             <span >{{ item.artists[0].name}}</span>
         </div>
       </div>
     </router-link>
   </ul>
  </div>
</template>

<script>
export default {
  name:"Search",
  data(){
    return{//保存获得的参数
      Content:"",
      list:[]
    }
  },
  methods:{
    search(){//通过拼接地址方法,请求网络数据
      const URL = this.HOST + `/search?keywords=${this.Content}`;
        this.$axios.get(URL)
        .then(res => {
          if(res.data.error_code == 22001){
            alert('搜索数据不存在');
            return ;
          }
          this.list = res.data.result.songs
        })
        .catch(error => {
          console.log(error);
        })
    }
  }
}
</script>

<style scoped>

.search{
  background-color: #f4cccc;
}
.title{
  padding: 20px;
  overflow: hidden;
  clear: both;
}
button{
  background-color: #efafaf;
}

input{
width: 80%;
height: 30px;
line-height: 30px;
background: #f4cccc;
border: 1px solid #f4cccc;
padding-left: 10px;
float: left;
display: inline-block;
}

button{
float: left;
width: 15%;
height: 30px;
}

.list {
  word-wrap: break-word;
  -webkit-hyphens: auto;
  hyphens: auto;
  word-break: break-all;
  border-bottom: 1px solid #f4cccc;
  border-top: 1px solid #f4cccc;
}
.song {
  -webkit-box-pack: justify;
  -webkit-justify-content: space-between;
  justify-content: space-between;
  min-height: 55px;
  text-align: left;
}

li{
display: -webkit-box;
  display: -webkit-flex;
  display: flex;
  -webkit-box-orient: horizontal;
  -webkit-box-direction: normal;
  -webkit-flex-direction: row;
  flex-direction: row;
  -webkit-box-pack: start;
  -webkit-justify-content: flex-start;
  justify-content: flex-start;
  -webkit-box-align: center;
  -webkit-align-items: center;
  align-items: center;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
  min-height: 50px;
  border-bottom: 1px solid #f4cccc;
  padding-left: 10px;
}

.name {
  -webkit-box-flex: 1;
  -webkit-flex: 1;
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.name>span {
  font-weight: 400;
  display: block;
  font-size: 12px;
  color: #f69a9a;
}

</style>

主要调用的组件

  • 音乐播放组件
<template lang="html">
  <div class="search">
    <div class="title">
      <input type="text" name="" placeholder="请输入搜索内容" v-model="Content">
      <button  type="button" @click="search" name="button">搜索</button>
    </div>
    <ul class="list ">
     <router-link :key="index" tag="li" 
     :to="{name:'MusicPlay',
     params:{songid:item.id}}" 
     class="song" v-for="(item,index) in list">
       <div class="left">
         <div class="name ">
             <div>
                 <span>{{ item.name }}</span>
             </div>
             <span >{{ item.artists[0].name}}</span>
         </div>
       </div>
     </router-link>
   </ul>
  </div>
</template>

<script>
export default {
  name:"Search",
  data(){
    return{//保存获得的参数
      Content:"",
      list:[]
    }
  },
  methods:{
    search(){//通过拼接地址方法,请求网络数据
      const URL = this.HOST + `/search?keywords=${this.Content}`;
        this.$axios.get(URL)
        .then(res => {
          if(res.data.error_code == 22001){
            alert('搜索数据不存在');
            return ;
          }
          this.list = res.data.result.songs
        })
        .catch(error => {
          console.log(error);
        })
    }
  }
}
</script>

<style scoped>

.search{
  background-color: #f4cccc;
}
.title{
  padding: 20px;
  overflow: hidden;
  clear: both;
}
button{
  background-color: #efafaf;
}

input{
width: 80%;
height: 30px;
line-height: 30px;
background: #f4cccc;
border: 1px solid #f4cccc;
padding-left: 10px;
float: left;
display: inline-block;
}

button{
float: left;
width: 15%;
height: 30px;
}

.list {
  word-wrap: break-word;
  -webkit-hyphens: auto;
  hyphens: auto;
  word-break: break-all;
  border-bottom: 1px solid #f4cccc;
  border-top: 1px solid #f4cccc;
}
.song {
  -webkit-box-pack: justify;
  -webkit-justify-content: space-between;
  justify-content: space-between;
  min-height: 55px;
  text-align: left;
}

li{
display: -webkit-box;
  display: -webkit-flex;
  display: flex;
  -webkit-box-orient: horizontal;
  -webkit-box-direction: normal;
  -webkit-flex-direction: row;
  flex-direction: row;
  -webkit-box-pack: start;
  -webkit-justify-content: flex-start;
  justify-content: flex-start;
  -webkit-box-align: center;
  -webkit-align-items: center;
  align-items: center;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
  min-height: 50px;
  border-bottom: 1px solid #f4cccc;
  padding-left: 10px;
}

.name {
  -webkit-box-flex: 1;
  -webkit-flex: 1;
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.name>span {
  font-weight: 400;
  display: block;
  font-size: 12px;
  color: #f69a9a;
}

</style>
  • 视频播放组件
<template>
    <div class="play">
      <div class="player-box">
        <video-player
          class="video-player vjs-custom-skin"
          ref="VideoPlay"
          :playsinline="true"
          :options="playerOptions"
        ></video-player>
      </div>
    </div>
  </template>
  <script>
  export default {
    props: {
      videoUrl: {
        type: String,
        required: true,
      },
      coverUrl: {
        type: String,
        required: true,
      },
    },
    data() {
      return {
        playerOptions: {
          playbackRates: [0.7, 1.0, 1.5, 2.0], //播放速度
          autoplay: false, //如果true,浏览器准备好时开始回放。
          muted: false, // 默认情况下将会消除任何音频。
          loop: false, // 导致视频一结束就重新开始。
          preload: "auto", // auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
          language: "zh-CN",
          aspectRatio: "16:9", // 将播放器置于流畅模式
          fluid: true, // 实现按比例缩放以适应容器。
          sources: [
            {
              type: "", //种类
              src: this.videoUrl, //url地址
            },
          ],
          poster: this.coverUrl, //你的封面地址
          controlBar: {
            timeDivider: true,
            durationDisplay: true,
            remainingTimeDisplay: false,
            fullscreenToggle: true, //全屏按钮
          },
        },
      };
    },
    watch: {
      videoUrl(val) {
        this.playerOptions.sources[0].src = val;
      },
      coverUrl(val) {
        this.playerOptions.poster = val;
      },
    },
  };
  </script>
  <style scoped>
  
  .play{
    background-color: #f4cccc;
    /* overflow-y:scroll ; ::-webkit-scrollbar*/
  }
  .header{
      padding:15px;
    background-color: #efafaf;
  }
  
  .movie{
      flex: 1;
      font-size: 20px;
  }
  
  .title{
      display: flex;
      text-align: center;
  }
  
  .left{
      font-size: 30px;
  }
  
  .right{
      font-size: 30px;
  }
  
  .songs{
      padding: 15px;
    margin-top: 40px;
  }
  
  .song-img{
      /* text-align: center; */
    width:345px;
    height:280px;
  }
  
  
  
  .songPlay{
      width: 100%;
    display: flex;
      /* text-align: center; */
    margin-top: 40px;
  }
  
  .songPlay audio{
      width: 100%;
  }
  
  .active{
    color: #222;
  }
  
  .author{
    font-size: 12px;
    color: #999;
  }
  
  </style>

项目不足

1.各类访问数据是从线上获取,缺少对数据层的开发与管理功能。

2.未实现用户的注册及登录页面,播放器的安全性较低。

3.用户可操作的功能较少。

个人小结

由于目前本项目的功能尚未达到理想版本,我想下次更新一下,再来进一步补充该项目的介绍并附上源码。