微信小程序开发案例 | 音乐播放器小程序(下)

阶段案例-音乐播放器小程序
01、逻辑实现
1 初始化参数
先初始化一些参数,在utils/tool.js文件中追加常量数组songs用于记录歌曲库。
tool.js文件代码如下:
1. // 歌曲库
2. const songs = [{
3. id: '001',
4. url: 'https://imc.ahnu.edu.cn/xy/songs/Serenade.mp3',
5. title: '小夜曲',
6. singer: '舒伯特',
7. poster: '/images/cover/night.jpg'
8. },
9. {
10. id: '002',
11. url: 'https://imc.ahnu.edu.cn/xy/songs/EndlessHorizon.mp3',
12. title: '无尽的地平线',
13. singer: '班得瑞',
14. poster: '/images/cover/horizon.jpg'
15. },
16. {
17. id: '003',
18. url: 'https://imc.ahnu.edu.cn/xy/songs/MoonSea.mp3',
19. title: '月光下的云海',
20. singer: '久石让',
21. poster: '/images/cover/sky.jpg'
22. }]
23.
24. // 导出公共函数和数据
25. module.exports = {
26. songs:songs27. }
● id:歌曲编号,确保每首歌编号互相不重复即可;
● url:歌曲文件的url地址;
● title:歌曲中文名称;
● singer:歌手或演奏者名称;
● poster:海报图片的路径地址。
需要注意的是,歌曲url地址为临时地址仅供学习使用,不保证永久有效,开发者也可以自行替换成其他歌曲资源url。
在index.js中添加一些初始参数,代码修改如下:
1. // index.js
2. // 导入公共函数和数据
3. const tool = require('../../utils/tool')
4.
5. // ===================
6. // 音乐播放器初始化参数
7. // ===================
8. let songs = tool.songs// 获取的音乐库
9. let index = 0// 当前歌曲序号
10. let bgm = null// 背景音乐管理器
11. let dsq = null// 定时器
12.
13. Page({
14. /**
15. * 页面的初始数据
16. */
17. data: {
18. isPlaying: false, //当前是否在播放(默认否)
19. needleStatus: 'needle-stop',//指针状态(默认停止)
20. discAnimation: '',//唱片转动动画样式(默认无)
21. song: songs[index], //当前是哪首歌
22. currentTime: 0, //当前播放到第几秒(默认第0秒)
23. duration: 60//音乐总时长(默认共60秒)
24. },
25. …26. })
2 动态化页面
将页面中原本写成固定值的地方利用初始化参数改为可动态变化的效果。
index.wxml代码修改如下:
1. <!--index.wxml-->
2. <!-- 1整体容器 -->
3. <view class="container">
4. <!-- 2 标题区域 -->
5. <view class="title">
6. <text>{{song.title}}({{song.singer}})</text>
7. </view>
8.
9. <!-- 3 唱片区域 -->
10. <view class="playBox">
11. <!-- 3-1 指针 -->
12. <image class="needle {{needleStatus}}" src="/images/player/needle.png" mode="widthFix"></image>
13. <!-- 3-2 唱片 -->
14. <view class="discBox">
15. <!-- 3-2-1 黑胶唱片边框(无修改,内容略) -->
16. <!-- 3-2-2 唱片中间圆形海报 -->
17. <image class="disc-cover {{discAnimation}}" src="{{song.poster}}"></image>
18. </view>
19. </view>
20.
21. <!-- 4 进度条区域 -->
22. <view class="progress">
23. <!-- 4-1 左侧当前播放时间 -->
24. <view>{{currentTime}}</view>
25. <!-- 4-2 中间进度条 -->
26. <slider backgroundColor='#fff' min='0' max='{{duration}}' value="{{currentTime}}" block-size="12" activeColor="#fff" bindchange="changeTime"></slider>
27. <!-- 4-3 右边歌曲时长 -->
28. <view>{{duration}}</view>
29. </view>
30.
31. <!-- 5 按钮区域(无修改,内容略)-->
32. </view>
■ 图6-19 动态显示页面内容
此时进度条左右两侧的时间显示效果不是很好,希望可以格式化为“mm:ss”的形式,例如总时长320秒可以显示为“05:20”。考虑可以使用wxs文件可以快速帮忙将页面上的数据格式化。
wxs是一种可以在页面上处理数据的脚本文件,语法和js文件类似,由于初学者入门用的非常少,因此没有详细介绍。这里可以初步尝试,开发者如有兴趣后续也可以进一步学习。
在公共函数文件夹utils中新建filters.wxs文件来进行格式过滤,代码如下:
1. // 格式化时间显示
2. function formatTime(value) {
3. // 参数默认原单位是秒,这里先获取分
4. var m = Math.floor(value / 60)
5. // 获取剩余秒
6. var s = value % 60
7. // 格式化分和秒,显示2位数字
8. if (m < 10) m = '0' + m
9. if (s < 10) s = '0' + s
10. // 输出格式化后的结果
11. return m + ':' + s
12. }
13.
14. // 导出公共函数和数据
15. module.exports = {
16. formatTime: formatTime17. }
1. <!-- 引用wxs -->
2. <wxs module="filters" src="../../utils/filters.wxs"></wxs>
3. <!--index.wxml-->4. …
1. <!-- 4 进度条区域 -->
2. <view class="progress">
3. <!-- 4-1 左侧当前播放时间 -->
4. <view>{{filters.formatTime(currentTime)}}</view>
5. <!-- 4-2 中间进度条(无修改,内容略) -->
6. <!-- 4-3 右边歌曲时长 -->
7. <view>{{filters.formatTime(duration)}}</view>
8. </view>

■ 图6-20 格式化时间显示
此时开发者可以尝试手动修改index.js文件顶部初始化参数中的index,就可以看到不同歌曲的显示效果了,如下图所示。

■ 图6-21 手动切换歌曲下标查看显示效果
3 播放和暂停音乐
音乐播放有两种模式:前台播放和后台背景音乐播放。前台播放指的是只能在小程序页面打开的状态下播放,一旦退出就会停止;后台播放指的是小程序页面隐藏状态下仍可在手机后台持续播放,例如网易云音乐APP就是允许后台背景音乐播放。所以为了更好的用户体验效果,我们也考虑使用后台背景音乐播放模式。
在index.js的onLoad()函数中声明全局唯一的后台背景音乐播放管理器,代码如下:
1. // index.js
2. …
3. Page({
4. …,
5. /**
6. * 生命周期函数--监听页面加载
7. */
8. onLoad: function (options) {
9. // 获取背景音乐管理器
10. bgm = wx.getBackgroundAudioManager()
11. },
12. …13. })
● 播放:bgm.play()或初始设定了bgm.url属性值也会自动播放;
● 暂停:bmg.pause()。
所以接下来开始制作播放和暂停按钮的点击事件,修改index.wxml代码如下:
1. <!-- 5 按钮区域 -->
2. <view class="btnBox">
3. <!-- 5-1 上一首按钮(暂无修改,内容略) -->
4. <!-- 5-2 (1) 播放按钮 -->
5. <image wx:if="{{!isPlaying}}" class="bigBtn" src="/images/player/btn_play.png" bindtap="playBgm"></image>
6. <!-- 5-2 (2) 暂停按钮 -->
7. <image wx:else class="bigBtn" src="/images/player/btn_pause.png" bindtap="pauseBgm"></image>
8. <!-- 5-3 下一首按钮(暂无修改,内容略) -->
9. </view>
1. // index.js
2. …
3. Page({
4. …,
5. /**
6. * 自定义函数--开始播放歌曲
7. */
8. playBgm: function () {
9. // 设置歌曲信息
10. bgm.src = songs[index].url
11. bgm.title = songs[index].title
12. bgm.singer = songs[index].singer
13. },
14. /**
15. * 自定义函数--暂停音乐
16. */
17. pauseBgm: function () {
18. // 暂停音乐
19. bgm.pause()
20. },
21. …22. })
{
"pages": ["pages/index/index"],
…其它配置属性略…
"requiredBackgroundModes": ["audio"]
}
此时歌曲就已经可以播放了,但是动画效果和时间显示等都还没跟着变化。
在index.js文件中封装自定义函数updateStatus()用于切换播放和暂停状态下初始参数的取值变化,代码如下:
1. // index.js
2. …
3. Page({
4. …,
5. /**
6. * 自定义函数--更新状态参数
7. */
8. updateStatus: function (isPlaying) {
9. // 更新参数
10. this.setData({
11. isPlaying: isPlaying, //当前是否在播放
12. needleStatus: isPlaying ? 'needle-play' : 'needle-stop',//指针状态
13. discAnimation: isPlaying ? 'disc-cover-animation' : ' ',//唱片转动动画样式
14. song: songs[index], //当前是哪首歌
15. duration: Math.round(bgm.duration), //歌曲时长
16. currentTime: Math.round(bgm.currentTime) //当前播放时间
17. })
18. },
19. …20. })
修改index.js的onLoad()函数,追加以下内容:
1. // index.js
2. …
3. Page({
4. …,
5. /**
6. * 生命周期函数--监听页面加载
7. */
8. onLoad: function (options) {
9. // 获取背景音乐管理器
10. bgm = wx.getBackgroundAudioManager()
11. // 播放歌曲
12. this.playBgm()
13.
14. // 监听到开始播放
15. bgm.onPlay(() => {
16. console.log('***onPlay正在播放***')
17. // 更新参数
18. this.updateStatus(true)
19. })
20.
21. // 监听到播放暂停
22. bgm.onPause(() => {
23. console.log('***onPause播放暂停***')
24. // 更新参数
25. this.updateStatus(false)
26. })
27. },
28. …29. })
现在就可以尝试播放了,运行效果如图6-22所示。

■ 图6-22 播放/暂停音乐功能实现
4 切换音乐
现在制作上一首和下一首按钮的点击事件,修改index.wxml代码如下:
1. <!-- 5 按钮区域 -->
2. <view class="btnBox">
3. <!-- 5-1 上一首按钮 -->
4. <image class="smallBtn" src="/images/player/btn_prev.png" bindtap="prev"></image>
5. <!-- 5-2 (1) 播放按钮(暂无修改,内容略) -->
6. <!-- 5-2 (2) 暂停按钮(暂无修改,内容略) -->
7. <!-- 5-3 下一首按钮 -->
8. <image class="smallBtn" src="/images/player/btn_next.png" bindtap="next"></image>
9. </view>
1. // index.js
2. …
3. Page({
4. …,
5. /**
6. * 自定义函数--上一首
7. */
8. prev: function () {
9. // 更新当前歌曲序号
10. index = index == 0 ? songs.length - 1 : index - 1
11. // 播放歌曲
12. this.playBgm()
13. },
14.
15. /**
16. * 自定义函数--下一首
17. */
18. next: function () {
19. // 更新当前歌曲序号
20. index = index == songs.length - 1 ? 0 : index + 1
21. // 播放歌曲
22. this.playBgm()
23. },
24. …25. })
现在就可以尝试歌曲切换了,运行效果如图6-23所示。

■图6-23 切换音乐功能实现
5 实时更新播放时间
需要注意的是,目前进度条左侧的时间并不会在播放状态下自动更新,只有暂停时才会突然切换显示到正确的时间点。这是因为bgm管理器的onPlay()监听只触发了一次,所以当前播放时间参数没有每秒跟着变化,直到暂停时触发了onPause()才又被更新了一次参数。
现在就可以用到定时器功能设置只要开始播放,就每秒更新一次当前时间显示。
首先在index.js中封装自定义函数toggleTimer(Boolean start)用于启动或关停定时器,代码如下:
1. // index.js
2. …
3. Page({
4. …,
5. /**
6. * 自定义函数--定时器启动/关闭
7. */
8. toggleTimer: function (start) {
9. // 关闭旧定时器
10. if (dsq != null) {
11. // 解除定时器
12. clearInterval(dsq)
13. // 定时器对象置为空值
14. dsq = null
15. }
16. // 启动新定时器
17. if (start) {
18. // 开启定时器
19. dsq = setInterval(() => {
20. // console.log(bgm.currentTime)
21. // 每秒更新一次当前播放时间
22. this.setData({
23. currentTime: Math.round(bgm.currentTime)
24. })
25. }, 1000)
26. }
27. },
28. …29. })
1. // index.js
2. …
3. Page({
4. …,
5. /**
6. * 生命周期函数--监听页面加载
7. */
8. onLoad: function (options) {
9. …
10. // 监听到开始播放
11. bgm.onPlay(() => {
12. console.log('***onPlay正在播放***')
13. // 更新参数
14. this.updateStatus(true)
15. // 启动定时器
16. this.toggleTimer(true)
17. })
18.
19. // 监听到播放暂停
20. bgm.onPause(() => {
21. console.log('***onPause播放暂停***')
22. // 更新参数
23. this.updateStatus(false)
24. // 停止定时器
25. this.toggleTimer(false)
26. })
27. },
28. …29. })

■ 图6-24 实时更新播放时间功能实现
6 手动更改进度条时间
当用户更改进度条指示点时希望歌曲也可以自动跳转到对应的时间继续播放。
首先为进度条组件追加监听事件,index.wxml代码修改如下:
1. …
2. <!-- 4 进度条区域 -->
3. <view class="progress">
4. …
5. <!-- 中间进度条 -->
6. <slider backgroundColor='#fff' min='0' max='{{duration}}' value="{{currentTime}}" block-size="12" activeColor="#fff" bindchange="changeTime"></slider>
7. …
8. </view>9. …
1. // index.js
2. …
3. Page({
4. …,
5. /**
6. * 自定义函数--更新歌曲时间
7. */
8. changeTime: function (e) {
9. // 获取进度条上的时间
10. let time = e.detail.value
11. // 更新页面显示的时间
12. this.setData({
13. currentTime: time
14. })
15. // 跳转到这个时刻继续播放
16. bgm.seek(time)
17. },
18. …19. })
■ 图6-25 手动更改进度条时间功能实现
7 播完自动切换下一首
当前歌曲播放完毕后就不再播放了,但是动画效果都还在继续。可以修改代码追加bgm管理器对于onEnd()播放结束状态的监听,一旦发现歌曲播完了就自动切换下一首继续播放。
修改index.js的onLoad()函数,新增onEnd()状态监听,修改代码如下:
1. // index.js
2. …
3. Page({
4. …,
5. /**
6. * 生命周期函数--监听页面加载
7. */
8. onLoad: function (options) {
9. …
10. // 监听到播放结束
11. bgm.onEnded(() => {
12. console.log('***播放结束onEnded***')
13. // 切换下一首歌曲
14. this.next()
15. })
16. …17. })
现在不用暂停就可以实时更新当前播放时间了,运行效果如图6-26所示。

■ 图6-26 播完自动切换下一首功能实现
至此整个阶段案例就全部完成了,运行效果如图6-27所示。

■ 图6-27 第6章阶段案例最终效果图
- 点赞
- 收藏
- 关注作者
评论(0)