音乐播放的设计实现

音乐播放的设计实现

具体代码请查看:mygithub

播放需要相关数据

因为很多界面都需要相关的逻辑,所以我是用状态管理工具来抽取播放相关的逻辑与数据,在页面中只显示数据,需要控制时也只是向状态管理工具dispach,我用的库为hy-event-store

数据如下:

state: {
    playSongList: [],
    playSongIndex: 0,

    id: 0,
    currentSong: {},
    currentTime: 0,
    durationTime: 0,
    lyricInfos: [],
    currentLyricText: "",
    currentLyricIndex: -1,

    isFirstPlay: true,

    isPlaying: false,
    playModeIndex: 0, // 0:顺序播放 1:单曲循环 2:随机播放
  },
      
      

playSongList是播放列表,这个取决与你是怎么进入音乐播放界面的,在不同的地方进入时,会向此stata传递相关的播放列表,当需要播放时,只需要dispach播放函数,传入歌曲id即可

页面播放的渲染

歌曲播放时,页面的数据会随之发生变化,比如currentTime, sliderValue等,我们可以在hy-event-store中监听歌曲的播放,在页面中拿这些改变的数据,随之改变,而有些改变需要做相应逻辑变化,比如进度条就需要:const sliderValue = currentTime / this.data.durationTime * 100,又因为变化其实不用那么频繁,我们可以对其进行适当节流,用underscore库即可

  audioContext.onTimeUpdate(() => {
          // 1.获取当前播放的时间
          ctx.currentTime = audioContext.currentTime * 1000
    
          // 2.匹配正确的歌词
          if (!ctx.lyricInfos.length) return
          let index = ctx.lyricInfos.length - 1
          for (let i = 0; i < ctx.lyricInfos.length; i++) {
            const info = ctx.lyricInfos[i]
            if (info.time > audioContext.currentTime * 1000) {
              index = i - 1
              break
            }
          }
          if (index === ctx.currentLyricIndex) return
    
          // 3.获取歌词的索引index和文本text
          // 4.改变歌词滚动页面的位置
          const currentLyricText = ctx.lyricInfos[index].text
          ctx.currentLyricText = currentLyricText
          ctx.currentLyricIndex = index
        })

歌词的处理

首先进行歌词解析,我们请求下来的歌词一般是 ’[00:58.65]他们说 要缝好你的伤 没有人爱小丑‘ 这种格式,用正则表达式解析,然后做相应处理后保存下来

// 正则(regular)表达式(expression): 字符串匹配利器

// [00:58.65]
const timeRegExp = /\[(\d{2}):(\d{2})\.(\d{2,3})\]/

export function parseLyric(lyricString) {
  const lyricStrings = lyricString.split("\n")

  const lyricInfos = []
  for (const lineString of lyricStrings) {
    // [00:58.65]他们说 要缝好你的伤 没有人爱小丑
    const timeResult = timeRegExp.exec(lineString)
    if (!timeResult) continue
    // 1.获取时间
    const minute = timeResult[1] * 60 * 1000
    const second = timeResult[2] * 1000
    const millsecondTime = timeResult[3]
    const millsecond = millsecondTime.length === 2 ? millsecondTime * 10: millsecondTime * 1
    const time = minute + second + millsecond

    // 2.获取歌词文
    const text = lineString.replace(timeRegExp, "")
    lyricInfos.push({ time, text })
  }

  return lyricInfos
}

歌词样式:歌词并不是像文本那样排列,应该保证正在播放的歌词时刻在屏幕中间,所以需要给歌词加上边距和下边距

 style="padding-top: {{index === 0 ? (contentHeight/2-66) : 0}}px; padding-bottom: {{ index === lyricInfos.length - 1 ? (contentHeight/2+66) : 0 }}px;"

并且当现在播放歌词要有不同样式

class="item {{currentLyricIndex === index ? 'active': ''}}"

歌词滚动:根据时间匹配,找到大于现在时间的前一天歌词的索引即可,最后一句歌词需要特殊处理,有了索引,就能让其滚到一定距离了

 if (!ctx.lyricInfos.length) return
          let index = ctx.lyricInfos.length - 1
          for (let i = 0; i < ctx.lyricInfos.length; i++) {
            const info = ctx.lyricInfos[i]
            if (info.time > audioContext.currentTime * 1000) {
              index = i - 1
              break
            }
          }
          if (index === ctx.currentLyricIndex) return
 this.setData({ currentLyricIndex, lyricScrollTop: currentLyricIndex * 35 })

播放模式

我们知道,播放模式有顺序播放,单曲循环和随机播放,我们有播放列表和歌曲索引,这个功能很好做

  switch (ctx.playModeIndex) {
        case 1:
        case 0: // 顺序播放
          index = isNext ? index + 1: index - 1
          if (index === length) index = 0
          if (index === -1) index = length - 1
          break
        case 2: // 随机播放
          index = Math.floor(Math.random() * length)
          break
      }

拖动进度

进度条是可以拖到的,我们可以监听onSliderChanging,onSliderChange事件,前者只改变当前时间和进度条,需要适当节流,且当前者拖动时不受歌曲播放的影响,可用isSliderChanging变量来实现;后者则用 audioContext.seek(currentTime / 1000)改变播放进度。

 //根据当前的滑块值, 计算出对应的时间
    const currentTime = value / 100 * this.data.durationTime
 onSliderChanging: throttle(function(event) {
    // 1.获取滑动到的位置的value
    const value = event.detail.value

    // 2.根据当前的值, 计算出对应的时间
    const currentTime = value / 100 * this.data.durationTime
    this.setData({ currentTime })

    // 3.当前正在滑动
    this.data.isSliderChanging = true
  }, 100),
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值