一、歌曲播放工具栏结构搭建

点击播放工具栏中的歌曲图片可以实现路由跳转到歌曲详情页 views/player/index.tsx

然后由于播放工具栏属于歌曲播放的一个功能,所以可以将该组件存放在player/app-player/index.tsx
然后开始进行结构搭建和样式配置分成,分成左,中,右三部分搭建结构



又因为播放器工具栏组件在多个页面中都有显示,且位置是固定的,所以可以把该组件放在App.tsx下作为固定结构显示

views/player/service存放播放器工具栏组件中用到的接口方法
views/player/store/player.ts下存放播放器工具栏中用到的数据
store/player.ts:
还要记得把playerReducer添加到store/index.ts中

interface IPlayerState { currentSong:any //当前要播放的歌曲 platSongList:any[] //播放列表 playSongIndex: number //当前播放的歌曲在列表中的索引 }
const initialState:IPlaterState={ currentSong:{} playSongList:{} //准备一份初始歌单数据在列表中 playSongIndex:-1 }
封装一个异步方法用于请求要播放的歌曲信息:
1、需要准备一个当前要播放的歌曲变量,一个播放列表,还要准备一个表示当前要播放的歌曲在列表中的索引的变量。 2、然后再收到其他组件中传入的歌曲id值时,需要先进行判断在播放列表中是否有当前要播放的歌曲,如果没有,则调用接口方法,获取歌曲信息,存入当前要播放的歌曲的变量currentSong中。并且将获取到的歌曲添加到播放列表中,更改播放列表。如果有,则利用索引,在播放列表中找到歌曲,赋值给currentSong。还要更改当前歌曲在列表中的索引。

!!!在异步方法中,我们用到了state中存储的变量playSongList,所以要利用 getState().reducer名.数据名 来拿到数据值。因此由于要用到getState(),所以要在方法前传入泛型<void,异步方法中接收的参数类型,{state:IRootState}>

然后再来书写异步方法逻辑。
封装一个用于播放歌曲的工具函数在utils下:
utils/handle-player.ts

在App.tsx中的useEffect中调用异步方法,并且传入歌曲id值

然后再在播放器工具栏组件中进行播放歌曲:
1、拿到音频组件对象
(这种比较简单的组件,获取其对象就利用useRef<HTML组件名Element>(null))

2、准备一些变量

3、拿到redux中存储的变量(当前要播放的歌曲)

4、引入工具方法用于执行播放,在useEffect中执行播放

然后要去实现根据当前的歌曲播放时间来匹配歌词:
在utils文件夹下封装一个工具函数用于解析歌词对象


service/player.ts下封装接口方法获取歌词信息,接受歌曲的id值作为参数。用于获取对应歌曲的歌词信息。

然后再到store/player.ts文件中调用接口方法,获取歌词信息

lyrics表示歌词数组 lyricIndex表示歌词在歌词数组中的索引



在播放器工具栏组件中,当音乐开始播放时,就会自动触发audio组件的onTimeUpdate事件






实现音乐暂停播放逻辑:


实现歌曲时间格式化处理:




实现歌曲的进度点击和拖曳处理:
1、用户直接在进度条组件上点击选择播放时间时,会触发onAfterChange事件


2、用户拖曳进度条,会触发slider组件的onChange事件


实现歌曲的播放模式匹配:
在store/recommend.ts下存储歌曲播放模式playMode
interface IPlayerState { playMode:number }
const initialState :IPlayerState ={ playMode:0 //默认是顺序播放 // 0:顺序播放 1:随机播放 2:单曲循环 }
在app-player/index.tsx中获取到redux中存储的歌曲播放模式。




点击切换播放模式的按钮,实现模式切换:

由于初始状态下,歌曲播放模式的值是0(顺序播放),所以之后每点击一下按钮,数值就会+1
直到数值>2,则说明歌曲播放模式又回到了顺序播放,数值变回0

并将播放模式的数值存储到redux中。
实现切换歌曲:
点击按钮,能够实现歌曲上一首,下一首的切换:


点击按钮,触发handleChangeMusic事件,如果此时点击的是:上一首,则传入参数false
如果点击的是:下一首,则传入参数true.

在store/player.ts下存储更改音乐的逻辑:
书写一个异步方法逻辑,先拿到state中的播放列表以及播放模式及当前播放的音乐在列表中的索引值:
利用getState().reducer名.数据名
所以要先给方法定义泛型 <void,方法接收的参数类型,{state:IRootState}>
1、如果当前的播放模式为随机播放,则要从播放列表中随机挑选音乐播放,随机得到播放音乐的索引,然后再去列表中对应歌曲播放。


2、如果当前的播放模式为单曲循环/顺序播放,点击上一首/下一首时就相当于是索引++/索引--
如果传入异步方法中的参数是false,则说明点击的是上一首,则索引--
如果传入异步方法的参数是true,则说明点击的是下一首,则索引++
如果当前要播放的歌曲的索引<0,则说明回调到了最后一首歌曲播放
如果当前要播放的歌曲的索引>播放列表的长度-1,则说明回调到了第一首歌曲播放

再将歌曲赋值给currentSong,且改变歌曲在列表中的索引
以及请求新的歌词

最后做音乐播放结束时的逻辑:音乐结束后触发audio组件的onEnded事件
1、当音乐播放结束后,需要先判断当前用户选择的播放模式是不是单曲循环,如果是单曲循环的话,就要将当前播放的音乐的时间重置为0,再次播放。

2、如果用户选择的播放模式不是单曲循环,则相当于歌曲直接播放下一首,相当于是执行了播放下一首的逻辑,调用函数handleChangeMusic,并传入参数true,表示播放下一首。
总结: 1、先完成组件的封装,组件的结构搭建,样式配置 2、封装接口方法,请求播放歌曲,redux中设置变量,异步方法,同步方法 3、获取从其他组件传来的歌曲id,先判断播放列表中是否包含要播放的歌曲,有=>则利用索引提取出歌曲 没有=>则调用接口获取歌曲信息,并添加至列表中,并且调用获取歌词的接口,获取对应的歌词信息,并对歌词做解析处理,封装解析歌词的工具函数 4、在组件中的useEffect中执行播放音乐 5、实现音乐暂停,播放逻辑 6、实现当音乐开始播放时,获取当前音乐播放的时间,计算当前音乐播放的进度,获取当前音乐播放的时间对应的歌词 7、实现歌曲时间的格式化处理,在工具文件夹中封装格式化逻辑 8、进度条组件处理,进度点击时的处理以及拖曳进度条时的处理 9、歌曲播放模式匹配,点击切换播放模式的按钮,实现模式切换 10、实现点击上一首,下一首的逻辑处理,调用redux中设置的异步方法,传入false/true 11、在redux中配置异步方法逻辑,如果此时是随机播放,则拿到索引的随机数,在列表中对应匹配的歌曲如果此时是单曲循环或者顺序播放,则判断此时点击的是上一首还是下一首,然后来决定当前播放的歌曲的索引++/-- 12、音乐切换后,也要重新对应歌词 13、实现音乐播放结束时的逻辑 需要判断此时是否为单曲循环,是的话则重置当前播放的歌曲的时间,重新播放,不是的话就直接播放下一首,调用执行播放下一首的函数。
在redux中的异步方法中拿到state中存储的数据,利用getState().reducer名.数据名 要用到getState(),就要先给方法定义泛型
<void,传入异步方法中的参数类型,{state:IRootState}