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

举报
TiAmoZhang 发表于 2025/11/03 10:50:26 2025/11/03
【摘要】 音乐播放器小程序

阶段案例-音乐播放器小程序

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. }
当前节选了3首歌曲作为数组元素,每个元素均包含5个属性,解释如下:

● 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.  })
其中第19行是用于切换指针状态的,needle-stop表示停止、needle-play表示播放;第20行是用于切换唱片动画效果的,无内容表示停止动画、disc-cover-animation表示旋转动画。

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. }
在首页index.wxml文件顶部对filters.wxs进行引用,代码如下:
1. <!-- 引用wxs -->
2. <wxs module="filters" src="../../utils/filters.wxs"></wxs>
3. <!--index.wxml-->4. …
此时就可以用filters.wxs写的formatTime()函数来格式化进度条区域左右两侧的时间显示效果了,index.wxml代码修改如下:
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>
现在就可以在index.js中新增函数playBgm()和pauseBgm()了,代码如下:
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. })
从微信客户端6.7.2版本开始,若想在小程序切换到后台仍可播放背景音频,还需要在app.json文件中配置requiredBackgroundModes属性。示例如下:
{
  "pages": ["pages/index/index"],
  …其它配置属性略…
  "requiredBackgroundModes": ["audio"]
}
requiredBackgroundModes属性与其它配置属性的代码位置顺序不限,开发版和体验版均可直接生效,但正式版还得审核后方可生效。

此时歌曲就已经可以播放了,但是动画效果和时间显示等都还没跟着变化。

在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. })
需要注意的是必须等歌曲已经播放后才能通过bgm管理器获取总时长和当前播放的时间点,所以需要等歌曲开始播放了才能触发onPlay()监听,然后再更新参数。不能直接在playBgm()函数里调用updateStatus(true)来更新参数。

现在就可以尝试播放了,运行效果如图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>
在index.js中新增函数prev()和next(),代码如下:
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. })
第10行表示如果当前是第一首歌曲,再切换上一首会自动跳回最后一首,否则正常序号-1;第20行表示如果当前已经是最后一首歌曲,再切换下一首会自动跳回第一首继续播放,否则正常序号+1。

现在就可以尝试歌曲切换了,运行效果如图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. })
修改index.js的onLoad()函数,追加以下内容:
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-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. …
在index.js中新增changeTime()函数,追加以下内容:
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所示。

■ 图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章阶段案例最终效果图


【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。