我是靠谱客的博主 自信大门,这篇文章主要介绍Vue2 - 网易云音乐项目笔记(基于Vant UI组件库)一、项目技术二、准备工作三、分析页面实现功能四、具体页面实现五、Play和Comment组件详解,现在分享给大家,希望可以做个参考。

目录

  • 一、项目技术
  • 二、准备工作
    • 1、初始化Vue项目
    • 2、配置Vant UI组件库
    • 3、下载并使用vue-router库
    • 4、接口API
    • 5、postcss插件
  • 三、分析页面实现功能
    • 1、路由页面准备
    • 2、封装axios网路请求
    • 3、公共组件
  • 四、具体页面实现
    • 1、Layout组件相关
    • 2、Home组件相关
      • ① 封装home相关axios请求
      • ② Home组件代码
    • 3、Search组件相关
      • ① 封装search相关axios请求
      • ②Search组件代码
    • 4、Comment组件相关
      • ① 封装comment相关axios请求
      • ②Comment组件代码
    • 5、Play组件相关
      • ① 封装play相关axios请求
      • ②Play组件代码
    • 6、API统一出口
  • 五、Play和Comment组件详解

一、项目技术

基于 Vue + Vant UI组件库 实现网易云音乐
Vant UI (有赞团队打造的移动端Vue组件库)地址:https://vant-contrib.gitee.io/vant/#/zh-CN

共项目共实现四个功能:主页Home、搜索页Search、评论页Comment、播放页Play
网易云手机端地址:https://y.music.163.com/m
在这里插入图片描述

二、准备工作

1、初始化Vue项目

① 安装Vue脚手架

复制代码
1
2
npm i @vue/cli -g

② 切换到要创建项目的目录,然后创建music项目

复制代码
1
2
vue create music

③ 启动项目

复制代码
1
2
npm run serve

2、配置Vant UI组件库

① 下载vant ui库
注意项:Vant组件库要下载配合Vue2版本的使用

复制代码
1
2
npm i vant@latest-v2

② 配置按需引入样式
在基于 vue-cli 的项目中使用 Vant 时,可以使用 unplugin-vue-components 插件,它可以自动引入组件,并按需引入组件的样式。
安装插件

复制代码
1
2
npm i unplugin-vue-components -D

配置插件:在 vue.config.js 文件中配置插件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const { VantResolver } = require('unplugin-vue-components/resolvers'); const ComponentsPlugin = require('unplugin-vue-components/webpack'); module.exports = { configureWebpack: { plugins: [ ComponentsPlugin({ resolvers: [VantResolver()], }), ], }, };

优点:减少代码体积
缺点:使用起来会变得繁琐一些

3、下载并使用vue-router库

① 下载vuex库(vue2中必须使用vue-router3版本)

复制代码
1
2
npm i vue-router@3

② 引入

复制代码
1
2
import VueRouter from "vue-router"

③ 使用VueRouter

复制代码
1
2
Vue.use(VueRouter)

④ 使用VueRouter之后,在创建vm的时候就可以传入一个router配置项

复制代码
1
2
3
4
5
6
new Vue({ //使用VueRouter之后,创建vm的时候可以传入一个router配置项 router, render:h=>h(App), }).$mount("#app")

4、接口API

项目地址:https://neteasecloudmusicapi.js.org/#/
文档地址:https://binaryify.github.io/NeteaseCloudMusicApi/#/
① 安装项目

复制代码
1
2
3
4
5
6
// 从github上克隆项目到本地 git clone git@github.com:Binaryify/NeteaseCloudMusicApi.git // 安装依赖包 npm install

② 运行项目

复制代码
1
2
node app.js

③ 本地地址
http://localhost:3000
在这里插入图片描述

5、postcss插件

webpack的一个插件,将要px转化为rex插件
中文官网:https://www.postcss.com.cn/
① 安装postcss

复制代码
1
2
npm install postcss

② 安装postcss-pxtorem(最新版本6.0与Vant不兼容,因此安装5.1.1版本)
postcss-pxtorem是postcss插件,把px转换成rem

复制代码
1
2
npm install postcss-pxtorem@5.1.1 --s-d

③ 根目录下创建postcss.config.js文件

复制代码
1
2
3
4
5
6
7
8
9
10
module.exports = { plugins: { 'postcss-pxtorem': { rootValue: 37.5, // 已设计稿宽度375px为例 vant用的是375的设计稿 propList: ['*'], }, }, };

三、分析页面实现功能

分析:
根据项目页面构成,我们要创建4个vue组件,分别是views/Home、views/Search、views/Comment、views/Play

页面之间通过路由跳转,因此要创建文件夹 router/index.js

页面所需要的数据是通过发送网络请求获取的,因此要创建untils/request.js文件,api/index.js作为统一出口

首页和搜索页的顶部和底部一样,可以将这部分提取为一个组件,因此要创建views/layout.vue

首页最新新闻和搜索页最佳匹配样式结构一样,因此也可以提取出来,创建components/songItem.vue组件

1、路由页面准备

layout属于一级路由,home和search属于二级路由;comment、play属于一级路由
src/router/index.js文件代码

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import Vue from "vue"; import VueRouter from "vue-router"; import Layout from "@/views/Layout"; import Home from "@/views/Home"; import Search from "@/views/Search"; import Play from "@/views/Play"; import Comment from "@/views/Comment" Vue.use(VueRouter); export default new VueRouter({ routes:[ { path:"/", redirect:'/layout', }, { path:"/layout", redirect:'/layout/home', component:Layout, children:[ { name:'home', path:'home', component:Home, meta:{ title:'首页' } },{ name:'search', path:'search', component:Search, meta:{ title:'搜索' } } ] }, { name:'play', path:'/play', component:Play, }, { name:'comment', path:'/comment', component:Comment } ] })

2、封装axios网路请求

新建untils/request.js

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
import axios from "axios"; // axios.create()创建一个axios对象 const request = axios.create({ //基础路径,发请求的时候,路径当中会出现api,不用你手写 baseURL:'http://localhost:3000', //请求时间超过5秒 timeout:5000 }); export default request

3、公共组件

components/songItem.vue组件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<template> <van-cell :id="id" :title="name" :label="autor" @click="getComment(id)"> <template #right-icon> <van-icon name="play-circle-o" class="search-icon" @touchstart ="playFn(id)"/> </template> </van-cell> </template> <script> export default { name:'SongItem', props:{name:String,autor:String,id:Number}, methods:{ playFn(id){ this.$router.push({ name:'play', query:{ id:id } }); console.log(id); }, getComment(id){ this.$router.push({ name:'comment', query:{ id } }) } } } </script>

四、具体页面实现

1、Layout组件相关

views/Layout/index.vue

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<template> <div> <!-- 顶部 --> <van-nav-bar :title="activeTitle" fixed/> <!-- 中间部分 --> <div class="main"> <!-- 缓存组件,当切换路由时不被销毁 --> <keep-alive include="Home,Search"> <router-view></router-view> </keep-alive> </div> <!-- 底部 --> <van-tabbar v-model="active"> <van-tabbar-item icon="home-o" to="/layout/home">首页</van-tabbar-item> <van-tabbar-item icon="search" to="/layout/search">搜索</van-tabbar-item> </van-tabbar> </div> </template> <script> import { ref } from 'vue'; export default { name:'Layout', data(){ return { activeTitle:this.$route.meta.title, } }, setup() { const active = ref(0); return { active, }; }, // 通过监听路由切换 watch:{ $route(){ this.activeTitle = this.$route.meta.title } } } </script> <style scoped> .main { padding-top: 56px; padding-bottom: 66px; } </style>

2、Home组件相关

① 封装home相关axios请求

api/home.js

复制代码
1
2
3
4
5
6
7
8
import request from "@/untils/request"; // 推荐音乐 export const hotMusic = (params)=>request({url:'/personalized',params}) // 最新音乐 export const newMusic = (params)=>request({url:'/personalized/newsong',params})

② Home组件代码

views/Home/index.vue

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<template> <div> <p class="title">推荐歌单</p> <van-row gutter="10" > <van-col span="8" v-for="m in hotMusic" :key="m.id"> <van-image width="100%" height="3rem" :src="m.picUrl"/> <p class="song_name">{{m.name}}</p> </van-col> </van-row> <p class="title">最新音乐</p> <SongItem v-for="n in newMusic" :key="n.id" :name="n.name" :autor="n.song.artists[0].name + n.name" :id="n.id"></SongItem> </div> </template> <script> import SongItem from "@/components/songItem.vue"; import {hotMusicAPI,newMusicAPI} from "@/api"; export default { name:'Home', data(){ return { hotMusic:[], newMusic:[], } }, components:{SongItem}, async created(){ // 获取推荐歌曲 const resHot= await hotMusicAPI({limit:6}) this.hotMusic = resHot.data.result; // 获取最新音乐 const resNew = await newMusicAPI({limit:20}); this.newMusic = resNew.data.result; }, } </script> <style> /* 标题 */ .title { padding: 0.266667rem 0.24rem; margin: 0 0 0.24rem 0; background-color: #eee; color: #333; font-size: 15px; } /* 推荐歌单 - 歌名 */ .song_name { font-size: 0.346667rem; padding: 0 0.08rem; margin-bottom: 0.266667rem; word-break: break-all; text-overflow: ellipsis; display: -webkit-box; /** 对象作为伸缩盒子模型显示 **/ -webkit-box-orient: vertical; /** 设置或检索伸缩盒对象的子元素的排列方式 **/ -webkit-line-clamp: 2; /** 显示的行数 **/ overflow: hidden; /** 隐藏超出的内容 **/ } </style>

3、Search组件相关

① 封装search相关axios请求

复制代码
1
2
3
4
5
6
7
8
9
import request from "@/untils/request"; //热词 export const hotSearch = ()=>request({url:'/search/hot/detail'}); // 关键词搜索 export const keywordSearch = (params) => request({url:"/cloudsearch",params})

②Search组件代码

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
<template> <div> <van-search v-model="searchValue" placeholder="请输入搜索关键词" shape="round"/> <!-- 热门搜索关键字 --> <div class="search_wrap" v-if="searchValue.length==0"> <p class="hot_title">热门搜索</p> <div class="hot_name_wrap"> <span class="hot_item" v-for="h in hotSearch" :key="h.score" @click="btn(h.searchWord)">{{h.searchWord}}</span> </div> </div> <!-- 搜索结果 --> <div class="search_wrap" v-else> <p class="hot_title">最佳匹配</p> <SongItem v-for="k in keywordSearch" :key="k.id" :name="k.name" :autor="k.ar[0].name + ' / '+ k.al.name" :id="k.id"></SongItem> </div> </div> </template> <script> import {hotSearchAPI,keywordSearchAPI} from "@/api"; import SongItem from "@/components/songItem.vue"; export default { name:'Search', data() { return { searchValue:'', // 搜索框的值 hotSearch:[], // 热词 keywordSearch:[] //关键词搜索结果 } }, components:{SongItem}, async created(){ //获取热搜词 const hotSearch = await hotSearchAPI(); this.hotSearch = hotSearch.data.data; }, methods:{ async btn(str){ this.searchValue = str; const keywordSearch = await keywordSearchAPI({keywords:this.searchValue,type:1}); this.keywordSearch = keywordSearch.data.result.songs; //点击热词不需要等待立即执行 setTimeout(()=>{ clearTimeout(this.timer); }) }, }, watch:{ // 通过监视searchValue的变化来获取输入框的内容 searchValue(){ if(this.searchValue.length==0) { return this. keywordSearch = [] }else{ //设置防抖减少向服务器发送请求。类似于王者荣耀回车 clearTimeout(this.timer); this.timer = setTimeout(async()=>{ const keywordSearch = await keywordSearchAPI({keywords:this.searchValue,type:1}); this.keywordSearch = keywordSearch.data.result.songs; },3000) } } } } </script> <style scoped> /* 热门搜索容器的样式 */ .search_wrap { padding: 0.266667rem; } /*热门搜索文字标题样式 */ .hot_title { font-size: 0.32rem; color: #666; } /* 热搜词_容器 */ .hot_name_wrap { margin: 0.266667rem 0; } /* 热搜词_样式 */ .hot_item { display: inline-block; height: 0.853333rem; margin-right: 0.213333rem; margin-bottom: 0.213333rem; padding: 0 0.373333rem; font-size: 0.373333rem; line-height: 0.853333rem; color: #333; border-color: #d3d4da; border-radius: 0.853333rem; border: 1px solid #d3d4da; } </style>

4、Comment组件相关

① 封装comment相关axios请求

复制代码
1
2
3
4
5
import request from "@/untils/request"; // 获取评论 export const getComment = (params) =>request ({url:"/comment/hot",params})

②Comment组件代码

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
<template> <div> <van-nav-bar title="评论" fixed left-arrow @click-left="$router.back()"/> <div> <div class="main" > <!-- 下拉刷新 --> <van-pull-refresh v-model="refreshing" @refresh="onRefresh" success-text="刷新成功"> <van-list v-model="loading" :finished="finished" finished-text="没有更多了" @load="onLoad"> <van-cell v-for="(c,index) in list" :key="index"> <div class="wrap" > <div class="img_wrap"> <img :src="c.user.avatarUrl" alt=""> </div> <div class="conent_wrap"> <div class="header_wrap" > <div class="info_wrap"> <p>{{c.user.nickname}}</p> <p>{{c.time}}</p> </div> <div>{{c.likedCount}}点赞</div> </div> <div class="footer_wrap"> {{c.content}} </div> </div> </div> </van-cell> </van-list> </van-pull-refresh> </div> </div> </div> </template> <script> import {getCommentAPI} from "@/api"; export default { name:'Comment', data(){ return { id : this.$route.query.id, commentsInfo:[], // 每次接收20个评论数据 page:1, // 页码 loading:false, // 下拉加载状态 finished:false, // 所有数据是否加载完成状态 refreshing:true, // 上拉加载状态 list:[] // 所有数据 } }, methods: { //获取数据 async getList(){ const getComment = await getCommentAPI({id:this.id,type:0,limit:20,offset:(this.page -1 )*20}); this.commentsInfo = getComment.data.hotComments; this.commentsInfo.forEach(obj=>this.list.push(obj)) this.loading = false; }, // 上拉刷新 async onLoad(){ console.log(this.list.length) if(this.loading){ this.getList(); this.page++; this.refreshing = false; } if(this.list.length %20 != 0) { this.loading = false; this.finished = true; } }, // 下拉刷新 async onRefresh(){ this.finished = false; this.loading = true; this.onLoad(); } }, } </script> <style scoped> .main { padding-top: 46px; } .wrap { display: flex; } .img_wrap { width: 0.8rem; height: 0.8rem; margin-right: 0.266667rem; } .img_wrap img { width: 100%; height: 100%; border-radius: 50%; } .conent_wrap { flex: 1; } .header_wrap { display: flex; } .info_wrap { flex: 1; } .info_wrap p:first-child { font-size: 0.373333rem; color: #666; } .info_wrap p:last-of-type { font-size: 0.24rem; color: #999; } .header_wrap div:last-of-type { color: #999; font-size: 0.293333rem; } .footer_wrap { font-size: 0.4rem; color: #333; } </style>

5、Play组件相关

① 封装play相关axios请求

复制代码
1
2
3
4
5
6
7
8
9
10
11
import request from "@/untils/request"; //获取音乐播放地址 export const getSongById = (params)=>request({url:"/song/url/v1",params}); //获取歌词 export const getLyricById = (params)=>request({url:'/lyric',params}); //获取歌曲详情 export const getMusicById = (params)=>request({url:'/song/detail',params})

②Play组件代码

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
<template> <div class="play"> <!-- 模糊背景(靠样式设置), 固定定位 --> <div class="song-bg" :style="`background-image: url();`"></div> <!-- 播放页头部导航 --> <div class="header"> <van-icon name="arrow-left" size="20" class="left-incon" @click="$router.back()"/> </div> <!-- 留声机 - 容器 --> <div class="song-wrapper"> <!-- 留声机本身(靠css动画做旋转) --> <div class="song-turn ani" :style="`animation-play-state:${playState ? 'running' : 'paused'}`"> <div class="song-img"> <!-- 歌曲封面 --> <img class="musicImg" :src="musicInfo.al.picUrl"/> </div> </div> <!-- 播放按钮 --> <div class="start-box" @click="audioStart"> <span class="song-start" v-show="!playState"></span> </div> <!-- 播放歌词容器 --> <div class="song-msg"> <!-- 歌曲名 --> <h2 class="m-song-h2"> <span class="m-song-sname">{{ musicInfo.name }}-{{musicInfo.ar[0].name}}</span > </h2> <!-- 留声机 - 唱臂 --> <div class="needle" :style="`transform: rotate(${needleDeg});`"></div> <!-- 歌词部分-随着时间切换展示一句歌词 --> <div class="lrcContent"> <p class="lrc">{{ curLyric }}</p> </div> </div> </div> <!-- 音乐播放地址 --> <audio ref="audio" preload="true" :src="songInfo.url" @timeupdate="timeupdate"></audio> </div> </template> <script> // 获取歌曲详情和 歌曲的歌词接口 import { getSongByIdAPI, getLyricByIdAPI,getMusicByIdAPI } from '@/api' export default { name: 'play', data() { return { playState: false, // 音乐播放状态(true暂停, false播放) id: this.$route.query.id, // 上一页传过来的音乐id songInfo: {}, // 歌曲信息 musicInfo:"", // 歌曲详情信息 lyric: {}, // 歌词枚举对象(需要在js拿到歌词写代码处理后, 按照格式保存到这个对象) curLyric: '', // 当前显示哪句歌词 lastLy: '' ,// 记录当前播放歌词 } }, computed: { needleDeg() { // 留声机-唱臂的位置属性 return this.playState ? '-7deg' : '-38deg' } }, async created(){ // 获取歌曲详情, 和歌词方法 const res = await getSongByIdAPI({id:this.id}) this.songInfo = res.data.data[0]; // 获取歌曲详情 const musicInfo =await getMusicByIdAPI({ids:this.id}); this.musicInfo = musicInfo.data.songs[0]; // 获取-并调用formatLyric方法, 处理歌词 const lyrContent = await getLyricByIdAPI({id:this.id}); const lyricStr = lyrContent.data.lrc.lyric this.lyric = this.formatLyric(lyricStr) // 初始化完毕先显示零秒歌词 this.curLyric = this.lyric[0] }, methods: { formatLyric(lyricStr) { // 可以看network观察歌词数据是一个大字符串, 进行拆分. let reg = /[.+?]/g // let timeArr = lyricStr.match(reg) // 匹配所有[]字符串以及里面的一切内容, 返回数组 console.log(timeArr); // ["[00:00.000]", "[00:01.000]", ......] let contentArr = lyricStr.split(/[.+?]/).slice(1) // 按照[]拆分歌词字符串, 返回一个数组(下标为0位置元素不要,后面的留下所以截取) console.log(contentArr); let lyricObj = {} // 保存歌词的对象, key是秒, value是显示的歌词 timeArr.forEach((item, index) => { // 拆分[00:00.000]这个格式字符串, 把分钟数字取出, 转换成秒 let ms = item.split(':')[0].split('')[2] * 60 // 拆分[00:00.000]这个格式字符串, 把十位的秒拿出来, 如果是0, 去拿下一位数字, 否则直接用2位的值 let ss = item.split(':')[1].split('.')[0].split('')[0] === '0' ? item.split(':')[1].split('.')[0].split('')[1] : item.split(':')[1].split('.')[0] // 秒数作为key, 对应歌词作为value lyricObj[ms + Number(ss)] = contentArr[index] }) // 返回得到的歌词对象(可以打印看看) console.log(lyricObj); return lyricObj }, // 监听播放audio进度, 切换歌词显示 timeupdate(){ // console.log(this.$refs.audio.currentTime) let curTime = Math.floor(this.$refs.audio.currentTime) // 避免空白出现 if (this.lyric[curTime]) { this.curLyric = this.lyric[curTime] this.lastLy = this.curLyric } else { this.curLyric = this.lastLy } }, // 播放按钮 - 点击事件 audioStart() { if (!this.playState) { // 如果状态为false this.$refs.audio.play() // 调用audio标签的内置方法play可以继续播放声音 } else { this.$refs.audio.pause() // 暂停audio的播放 } this.playState = !this.playState // 点击设置对立状态 }, }, } </script> <style scoped> /* 歌曲封面 */ .musicImg { position: absolute; top: 0; left: 0; bottom: 0; right: 0; width: 100%; margin: auto; width: 370rpx; height: 370rpx; border-radius: 50%; } /* 歌词显示 */ .scrollLrc { position: absolute; bottom: 280rpx; width: 640rpx; height: 120rpx; line-height: 120rpx; text-align: center; } .header { height: 50px; } .play { position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 1000; } .song-bg { background-color: #161824; background-position: 50%; background-repeat: no-repeat; background-size: auto 100%; transform: scale(1.5); transform-origin: center; position: fixed; left: 0; right: 0; top: 0; height: 100%; overflow: hidden; z-index: 1; opacity: 1; filter: blur(25px); /*模糊背景 */ } .song-bg::before{ /*纯白色的图片做背景, 歌词白色看不到了, 在背景前加入一个黑色半透明蒙层解决 */ content: " "; background: rgba(0, 0, 0, 0.5); position: absolute; left: 0; top: 0; right: 0; bottom:0; } .song-wrapper { position: fixed; width: 247px; height: 247px; left: 50%; top: 50px; transform: translateX(-50%); z-index: 10001; } .song-turn { width: 100%; height: 100%; background: url("./img/bg.png") no-repeat; background-size: 100%; } .start-box { position: absolute; width: 156px; height: 156px; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); display: flex; justify-content: center; align-items: center; } .song-start { width: 56px; height: 56px; background: url("./img/start.png"); background-size: 100%; } .needle { position: absolute; transform-origin: left top; background: url("./img/needle-ab.png") no-repeat; background-size: contain; width: 73px; height: 118px; top: -40px; left: 112px; transition: all 0.6s; } .song-img { width: 154px; height: 154px; position: absolute; left: 50%; top: 50%; overflow: hidden; border-radius: 50%; transform: translate(-50%, -50%); } .m-song-h2 { margin-top: 20px; text-align: center; font-size: 18px; color: #fefefe; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } .lrcContent { margin-top: 50px; } .lrc { font-size: 14px; color: #fff; text-align: center; } .left-incon { position: absolute; top: 10px; left: 10px; font-size: 24px; z-index: 10001; color: #fff; } .ani { animation: turn 5s linear infinite; } @keyframes turn { 0% { -webkit-transform: rotate(0deg); } 100% { -webkit-transform: rotate(360deg); } } </style>

6、API统一出口

API/index.js

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 统一出口 import {hotMusic,newMusic} from "@/api/home"; import {hotSearch,keywordSearch} from "@/api/search"; import { getSongById,getLyricById,getMusicById} from "@/api/play"; import {getComment} from "@/api/comment"; //导出推荐歌单方法 export const hotMusicAPI = hotMusic //导出新歌方法 export const newMusicAPI = newMusic //导入热搜 export const hotSearchAPI = hotSearch; //导入关键词搜索 export const keywordSearchAPI = keywordSearch; //播放音乐 export const getSongByIdAPI = getSongById; //获取歌词 export const getLyricByIdAPI = getLyricById; //获取歌曲详情 export const getMusicByIdAPI = getMusicById; //获取评论 export const getCommentAPI = getComment;

五、Play和Comment组件详解

在做项目的过程中,感觉Play组件歌词同步逻辑有点复杂,还有Comment组件上拉和下拉刷新也有点难,因此专门写了两篇详解,可以看一下~~~
Play组件详解:Play组件播放音乐并实现同步一次显示一行歌词
Comment组件详解:Comment组件评论页上拉和下拉刷新

觉得有用,记得点赞收藏哦~~

最后

以上就是自信大门最近收集整理的关于Vue2 - 网易云音乐项目笔记(基于Vant UI组件库)一、项目技术二、准备工作三、分析页面实现功能四、具体页面实现五、Play和Comment组件详解的全部内容,更多相关Vue2内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(99)

评论列表共有 0 条评论

立即
投稿
返回
顶部