播放器组件
如上图,这次要做的播放器效果图如上;大体的功能如下:
- 第一张图:播放器界面
- 模糊背景
- 圆形旋转唱片
- 当前歌词
- 进度条
- 播放模式按钮
- 上一首、下一首按钮
- 播放、暂停按钮
- 搜藏(保存到本地)
第二张图片:歌词界面
歌词界面和播放器界面可以互相左右滑动切换。功能如下
- 最下边的功能和播放器界面的功能一致
- 展示整个歌词,提示当前歌词,并随着歌曲时间滚动歌词
- 第三张图片:歌手详情歌曲列表界面,点击第一、二张图片的左上角的按钮,显示的本页面
- 之前做的歌手详情和列表功能
- 最底部有一个迷你的播放条
- 展示歌曲的简单信息
- 播放、暂停按钮,还有一个圆圈进度条,表示当前歌曲播放的进度
- 当前正在播放的歌曲列表按钮
1. 拆分全局状态共享数据
由于这个播放器组件可以在很多页面都能打开,并控制,所以播放器的数据是共享数据。因此我们要把这些基础数据抽象出来用vuex来存储
src/store/state.js 抽象要管理的状态数据
import { playMode } from '../common/js/config'
const state = {
/* 歌手页面跳转到歌手详情页面传递的参数 */
singer: {},
/* 播放器播放状态:播放?暂停 */
playing: false,
/* 全屏模式 ? 迷你模式 */
fullScreen: false,
/* 原始播放列表数据 */
playlist: [],
/* 顺序列表:支持2种:1. 顺序播放(和playlist顺序一致),2. 随机播放 */
sequenceList: [],
/* 播放模式:支持三种:1. 随机 2.顺序 3.单曲循环 */
mode: playMode.sequence,
/* 当前正常播放的歌曲索引 */
currentIndex: -1
}
export default state
由于上面播放模式是固定的几个常量,所以我们把它提取出来放到一个配置文件中
src/common/js/config.js
/** ====================================
* 我们把项目中一些配置放到一个文件中
* ====================================
*/
/**
* 播放模式
* 1. 顺序
* 2. 循环
* 3. 随机
* @type {{sequence: number, loop: number, random: number}}
*/
export const playMode = {
sequence: 0,
loop: 1,
random: 2
}
状态数据有了。接下来我们要写怎么获取数据,也就是 getters
getters中对获取数据做一个简单的封装(也可以做一些复杂的计算),也相当于一个计算属性
src/store/getters.js
/** 用于获取数据 */
export const singer = state => state.singer
export const playing = state => state.playing
export const fullScreen = state => state.fullScreen
export const playlist = state => state.playlist
export const sequenceList = state => state.sequenceList
export const mode = state => state.mode
export const currentIndex = state => state.currentIndex
能获取数据了,那么怎么修改数据呢? 编写 mutations
在编写mutations之前,为了方便获取和操作方法,把方法名定义常量type
src/store/mutations-types.js
export const SET_SINGER = 'SET_SINGER'
export const SET_PLAYING = 'SET_PLAYING'
export const SET_FULL_SCREEN = 'SET_FULL_SCREEN'
export const SET_PLAYLIST = 'SET_PLAYLIST'
export const SET_SEQUENCE_LIST = 'SET_SEQUENCE_LIST'
export const SET_MODE = 'SET_MODE'
export const SET_CURRENT_INDEX = 'SET_CURRENT_INDEX'
在mutations中可以提交非异步的数据。 src/store/mutations.js
import * as types from './mutations-types'
const mutations = {
[types.SET_SINGER] (state, singer) {
state.singer = singer
},
[types.SET_PLAYING] (state, playing) {
state.playing = playing
},
[types.SET_FULL_SCREEN] (state, fullScreen) {
state.fullScreen = fullScreen
},
[types.SET_PLAYLIST] (state, playlist) {
state.playlist = playlist
},
[types.SET_SEQUENCE_LIST] (state, sequenceList) {
state.sequenceList = sequenceList
},
[types.SET_MODE] (state, mode) {
state.mode = mode
},
[types.SET_CURRENT_INDEX] (state, currentIndex) {
state.currentIndex = currentIndex
}
}
在action中可以封装对多个mutations的提交,也可以提交异步数据,但是这里暂时还用不上,等待编写细节的时候如有用到再添加。 src/store/action.js
暂无数据
2. 初版播放器应用架子
问题:
- 先做一个播放器的初始骨架组件
这个骨架组件在哪里引用?才能被多个入口控制呢?
由于该组件被多个入口控制,就说明这个也是一个全局的,那么放在App.vue中,其他入口控制 根据改变vuex中的数据来控制播放器的行为。
3. 从这一张开始,先看完视频然后再靠着记忆和理解,完成本章节所展示的内容。包括css。 下一章节肯定会对这一章节进行大量的修改,这些都不要紧。有思路就好
由于这个是一个业务组件,先建立初始的框架页面 src/components/player/player.vue
初始架子所要做的功能就是:
- 有播放列表的时候才显示播放组件
- 此时播放器是全屏还是迷你
<template>
<!--主要分为两大块内容:1. 全屏播放器 2, 迷你播放器-->
<div class="player" v-show="playlist.length >0">
<div class="normal-player" v-show="fullScreen">
播放器
</div>
<div class="mini-player" v-show="!fullScreen">
迷你播放器
</div>
</div>
</template>
<script type="text/ecmascript-6">
import { mapGetters } from 'vuex'
export default {
data () {
return {}
},
computed: {
...mapGetters([
'fullScreen',
'playlist'
])
}
}
</script>
<style scoped lang="stylus" rel="stylesheet/stylus">
@import "~common/stylus/variable"
@import "~common/stylus/mixin"
.player {
position: absolute;
top: 0;
right: 0;
left: 0;
bottom: 0;
background $color-background
.mini-player {
position absolute
bottom 0
right 0
left 0
height 60px
background $color-highlight-background
}
}
</style>
在App中引用 src/App.vue
<template>
<div id="app">
<m-header></m-header>
<tab></tab>
<keep-alive>
<router-view></router-view>
</keep-alive>
<player></player>
</div>
</template>
<script>
// 组件首字母大写,因为组件以css命名的
import MHeader from 'components/m-header/m-header'
import Tab from 'components/tab/tab'
import Player from 'components/player/player'
export default {
name: 'app',
components: {MHeader, Tab, Player},
data () {
return {}
}
}
</script>
这个时候,由于播放列表没有数据,所以页面不会有任何的变化。接下来,接着上一个大章节的,歌手详情歌曲列表中点击一首歌曲,然后打开这个播放器页面。
这里需要注意的是:之前的歌曲列表组件里面 引用了一个 songList组件,这个组件里面只做最基础的列表渲染,不做任何业务,因为被多个地方引用? 所以需要先对该该组件进行功能增加。
src/base/song-list/song-list.vue 派发点击事件
点击的是列表中的一首歌曲列表,所以需要回调以下参数
- 当前歌曲信息
- 当前歌曲在列表中的索引
src/components/music-list/music-list.vue 中监听点击事件,并更改vuex中的相关状态。
这里需要更改多个状态才行
- 播放列表
- 原始数据列表(因为我们有播放模式)
- 当前歌曲的索引
- 播放器全屏还是迷你模式(在这个场景中,点击歌曲就应该是播放了。跳转到播放列表)
- 播放状态
简单的就不记录了,以后只记录关键的思路
由于这里第二步骤,控制播放器而修改状态的时候要提交多个mutations,所以这里使用action来封装这个操作
src/store/action.js
import * as types from './mutations-types'
// 官方语法 https://vuex.vuejs.org/zh-cn/actions.html
export const selectPlay = function ({commit, state}, {list, index}) {
// 先暂时播放列表和排序列表一致
// 且 打开全屏播放器模式,并且设置为播放状态
commit(types.SET_SEQUENCE_LIST, list)
commit(types.SET_PLAYLIST, list)
commit(types.SET_CURRENT_INDEX, index)
commit(types.SET_FULL_SCREEN, true)
commit(types.SET_PLAYING, true)
}
在music-list中使用action提交
src/components/music-list/music-list.vue
在song-list组件上使用selectItem 监听点击事件。并调用actions
// 使用语法糖
// node_modules/vuex/types/helpers.d.ts 在这个文件中 有语法糖的名称
import { mapActions } from 'vuex'
export default {
methods: {
...mapActions([
'selectPlay'
]),
selectItem (item, index) {
// 这里提交的时候 一定要提交对象,因为只能提交一个参数
this.selectPlay({
list: this.songs,
index
})
}
}