小程序项目学习--第八章:播放页功能-代码重构-分包优化-打包发布

第八章:播放页功能-代码重构-分包优化-打包发布

01_(掌握)播放页-歌词分页的上下滚动区域

隐藏滚动条

.lyric-list ::-webkit-scrollbar {
    
    
  display: none;
}

歌词分页的上下滚动区域动态设置样式

思考什么时候是第一句歌词,什么时候是最后一句歌词

  <swiper-item>
    <scroll-view class="lyric-list" scroll-y>
    <block wx:for="{
    
    {lyricInfos}}" wx:key="time">
      <view 
          class="item {
    
    {currentLyricIndex === index ? 'active': ''}}"
          style="padding-top: {
    
    {index === 0 ? (contentHeight/2-66) : 0}}px; padding-bottom: {
    
    { index === lyricInfos.length - 1 ? (contentHeight/2+66) : 0 }}px;"
        >
          {
    
    {
    
    item.text}}
        </view>
    </block> 
    </scroll-view>
  </swiper-item>

02_(掌握)播放页-歌曲播放过程中歌词的滚动

索引是超级重要的

scroll-view中scroll-top设置竖向滚动的位置 scroll-top=“{ {lyricScrollTop}}” scroll-with-animatio动画 样式 class=“item { {currentLyricIndex === index ? ‘active’: ‘’}}”

 <swiper-item>
    <scroll-view class="lyric-list" scroll-y scroll-top="{
    
    {lyricScrollTop}}" scroll-with-animation>
    <block wx:for="{
    
    {lyricInfos}}" wx:key="time">
      <view 
          class="item {
    
    {currentLyricIndex === index ? 'active': ''}}"
          style="padding-top: {
    
    {index === 0 ? (contentHeight/2-66) : 0}}px; padding-bottom: {
    
    { index === lyricInfos.length - 1 ? (contentHeight/2+66) : 0 }}px;"
        >
          {
    
    {
    
    item.text}}
        </view>
    </block> 
    </scroll-view>
  </swiper-item>
data:{
    
    
    lyricScrollTop:0
}

onLoad(options) {
    
    
	this.setData({
    
    currentLyricText,currentLyricIndex:index,lyricScrollTop:35*index})
}



.lyric-list .item.active {
    
    
  color: #0f0;
  font-size: 32rpx;
}

03_(掌握)播放页-点击歌曲Item共享播放列表

获取播放列表,根据不同的点击位置,获取不同的播放列表

在进入播放页之前,要获取确定播放列表

点击事件即可以子组件绑定监听事件同时也可以在父组件中绑定监听事件,同时处理逻辑

而且播放列表的数据一定是共享的,不知是在播放页存在,很多地方也会使用播放列表的数据,所以我们使用store存储

image-20230131171215841

main-music页面设置store里的数据

import playerStore from "../../store/playerStore"

<song-item-v1 itemData="{
    
    {item}}" bindtap="onSongItemTap" />
  
   onSongItemTap(event) {
    
    
    //播放列表  this.data.recommendSongs
    playerStore.setState("playSongList", this.data.recommendSongs)
  },    
     

music-player获取store中的数据

import playerStore from "../../store/playerStore"

onLoad(){
    
    
     // 3.获取store共享数据
    playerStore.onState("playSongList", this.getPlaySongInfosHandler)
	}
  // ====================== store共享数据 ====================
  getPlaySongInfosHandler(value) {
    
        
    console.log(value)  ; 
  },
      
 onUnload() {
    
    
    playerStore.offStates("playSongList",this.getPlaySongInfosHandler)
 }

detail-song 获取列表

<!--pages/detail-song/detail-song.wxml-->
  <block wx:for="{
    
    {songInfo.tracks}}" wx:key="id">
    <song-item-v2 
      itemData="{
    
    {item}}" 
      index="{
    
    {index+1}}" 
      bindtap="onSongItemTap"
    />
import playerStore from "../../store/playerStore"
          
 // ================== wxml事件监听 ==================
  onSongItemTap() {
    
    
   //第一个参数是state中要共享的数据 第二个参数是服务器请求的数据需要存到的值
    playerStore.setState("playSongList", this.data.songInfo.tracks)
  },

共享数据列表方法

1.创建store

playerStore.js

import {
    
     HYEventStore } from 'hy-event-store'

const playerStore = new HYEventStore({
    
    
  state:{
    
    
    playSongList:[]
  }
})

export default playerStore

04_(掌握)播放页-点击歌曲Item共享歌曲的索引

获取歌单,成为歌单的播放列表

除了播放列表数据,我们还需要索引index(data自定义index列表)

1.保存数据加上index

import {
    
     HYEventStore } from 'hy-event-store'

const playerStore = new HYEventStore({
    
    
  state:{
    
    
    playSongList:[],
    playSongIndex:0  
  }
})

export default playerStore

2.通过自定义属性data-index=“{ {index}}”

main-music页面设置store里的数据和index

import playerStore from "../../store/playerStore"

<song-item-v1 itemData="{
    
    {item}}" bindtap="onSongItemTap" data-index="{
    
    {index}}" />
  
   onSongItemTap(event) {
    
    
    const index = event.currentTarget.dataset.index
    //播放列表  this.data.recommendSongs
    playerStore.setState("playSongList", this.data.recommendSongs)
    playerStore.setState("playSongIndex", index)
  },    
      
     

music-player获取store中的数据

import playerStore from "../../store/playerStore"
data:{
    
    
    playSongList:[],
   playSongIndex:0     
}



onLoad(){
    
    
     // 3.获取store共享数据
    playerStore.onStates(["playSongList","playSongIndex"], this.getPlaySongInfosHandler)
	}
  // ====================== store共享数据 ====================
  getPlaySongInfosHandler({
     
     playSongList, playSongIndex}) {
    
        
	if(playSongList){
    
    
        this.setData({
    
    playSongList})
    }
    if(playSongIndex !== undefined){
    
    
        this.setData({
    
    playSongIndex})
    }
  },
      
 onUnload() {
    
    
    playerStore.offStates(["playSongList","playSongIndex"],this.getPlaySongInfosHandler)
 }

05_(掌握)播放页-点击上一首和下一首的索引处理

绑定按钮点击事件

 <image class="btn prev" src="/assets/images/player/play_prev.png" bindtap="onPrevBtnTap" />
     
 <image class="btn next" src="/assets/images/player/play_next.png" bindtap="onNextBtnTap" />   
 
    // 点击了上一首
    onPrevBtnTap(){
    
    
     this.changeNewSong(false)
	 }    
     
  // 点击了下一首
    onNextBtnTap(){
    
    
     this.changeNewSong()
	 }   
     
  changeNewSong(isNext = true){
    
    
    // 1.获取之前的数据
    const length = this.data.playSongList.length
    let index = this.data.playSongIndex
    // 2.根据之前的数据计算最新的索引    
    index = isNext ? index + 1 :index - 1 
    if(index === length ) index = 0
     if(index === -1 ) index = length - 1  
    // 3.根据索引获取当前的歌曲信息
    const newSong = this.data.playSongList[index]
    console.log(newSong.id);
    // 4.保存最新的索引值
    playerStore.setState("playSongIndex",index)
  },     
     

06_(掌握)播放页-歌曲切换的函数抽取和细节处理

将播放歌曲的逻辑进行抽取setupPlaySong(id) 根据id请求歌曲

data:{
    
    
    isFirstPlay:true
}

onLoad(options) {
    
    
    // 0.获取设备信息
    this.setData({
    
    
      statusHeight: app.globalData.statusHeight,
      contentHeight: app.globalData.contentHeight
    })
    // 1.获取传入的id值
    // option 就是传递过来的数据
    // console.log(options);
    const id = options.id
    //2.播放歌曲
    this.setupPlaySong(id)
    //3.获取store共享数据
    playerStore.onStates(["playSongList","playSongIndex"], this.getPlaySongInfosHandler)
    }

//歌曲播放
setupPlaySong(id){
    
    
      this.setData({
    
    
      id
    })

	// 2.请求歌曲相关的数据
    // 2.1根据id获取歌曲的详情
    getSongDetail(id).then(res => {
    
    
      // console.log(res);
      this.setData({
    
    
        currentSong: res.songs[0],
        durationTime: res.songs[0].dt
      })
    })

    // 2.2根据id获取歌词的信息
    getSongLyric(id).then(res => {
    
    
      const lrcString =res.lrc.lyric
      // this.setData({
    
    
      //   lrcString: res.lrc.lyric
      // })
      const lyricInfos = parseLyric(lrcString)
      // console.log(lyricInfos)
       this.setData({
    
    lyricInfos})
    })
    // // 3.播放当前的歌曲
    audioContext.src = `https://music.163.com/song/media/outer/url?id=${
      
      id}.mp3`
    audioContext.autoplay = true

    // 4.监听播放的进度
    if(this.data.isFirstPlay){
    
    
       this.data.isFirstPlay = false 
   	   const throttleUpdateProgress = throttle(this.updateProgress,800,{
    
    leading:false,trailing:false})
       audioContext.onTimeUpdate(() => {
    
    
      // 1.更新歌曲的进度
      // if里面的变量为true的时候才执行
      if(!this.data.isSliderChanging && !this.data.isWaiting){
    
    
        throttleUpdateProgress()
      }
      // 2.匹配正确的歌词
      if(!this.data.lyricInfos.length) return
      // 初始默认值是最后一句歌词
      let index = this.data.lyricInfos.length -1
      for(let i =0; i<this.data.lyricInfos.length;i++){
    
    
        const info = this.data.lyricInfos[i]
        if(info.time > audioContext.currentTime * 1000){
    
    
          index = i-1
          break
        }
      }
      if(index === this.data.currentLricIndex) return
      // 3.获取歌词的索引index和文本text
      // 4.改变歌词滚动页面的位置
      const currentLyricText = this.data.lyricInfos[index].text
      this.setData({
    
    currentLyricText,currentLyricIndex:index,lyricScrollTop:35*index})
    })

    audioContext.onWaiting(() => {
    
    
      audioContext.pause()
    })
    audioContext.onCanplay(() => {
    
    
      audioContext.play()
    })
    }
}


changeNewSong(isNext = true){
    
    
    // 1.获取之前的数据
    const length = this.data.playSongList.length
    let index = this.data.playSongIndex
    // 2.根据之前的数据计算最新的索引    
    index = isNext ? index + 1 :index - 1 
    if(index === length ) index = 0
     if(index === -1 ) index = length - 1  
    // 3.根据索引获取当前的歌曲信息
    const newSong = this.data.playSongList[index]
    console.log(newSong.id);
      //将数据回到初始状态
      this.setData({
    
    currentSong:{
    
    },silderValue:0,currentTime:0,durationTime:0})
      //开始播放新的歌曲
      this.setupPlaySong(newSong.id)
    // 4.保存最新的索引值
    playerStore.setState("playSongIndex",index)
  },     

07_(掌握)播放页-歌曲结束自动播放下一首歌曲

监听歌曲播放完毕调用播放下一首歌曲的方法

 //歌曲播放
  setupPlaySong(id) {
    
    
    this.setData({
    
    
      id
    })

    // 2.请求歌曲相关的数据
    // 2.1根据id获取歌曲的详情
    getSongDetail(id).then(res => {
    
    
      // console.log(res);
      this.setData({
    
    
        currentSong: res.songs[0],
        durationTime: res.songs[0].dt
      })
    })

    // 2.2根据id获取歌词的信息
    getSongLyric(id).then(res => {
    
    
      const lrcString = res.lrc.lyric
      // this.setData({
    
    
      //   lrcString: res.lrc.lyric
      // })
      const lyricInfos = parseLyric(lrcString)
      // console.log(lyricInfos)
      this.setData({
    
    
        lyricInfos
      })
    })
    // // 3.播放当前的歌曲
    audioContext.src = `https://music.163.com/song/media/outer/url?id=${
      
      id}.mp3`
    audioContext.autoplay = true

    // 4.监听播放的进度
    if (this.data.isFirstPlay) {
    
    
      this.data.isFirstPlay = false
      const throttleUpdateProgress = throttle(this.updateProgress, 800, {
    
    
        leading: false,
        trailing: false
      })
      audioContext.onTimeUpdate(() => {
    
    
        // 1.更新歌曲的进度
        // if里面的变量为true的时候才执行
        if (!this.data.isSliderChanging && !this.data.isWaiting) {
    
    
          throttleUpdateProgress()
        }
        // 2.匹配正确的歌词
        if (!this.data.lyricInfos.length) return
        // 初始默认值是最后一句歌词
        let index = this.data.lyricInfos.length - 1
        for (let i = 0; i < this.data.lyricInfos.length; i++) {
    
    
          const info = this.data.lyricInfos[i]
          if (info.time > audioContext.currentTime * 1000) {
    
    
            index = i - 1
            break
          }
        }
        if (index === this.data.currentLricIndex) return
        // 3.获取歌词的索引index和文本text
        // 4.改变歌词滚动页面的位置
        const currentLyricText = this.data.lyricInfos[index].text
        this.setData({
    
    
          currentLyricText,
          currentLyricIndex: index,
          lyricScrollTop: 35 * index
        })
      })

      audioContext.onWaiting(() => {
    
    
        audioContext.pause()
      })
      audioContext.onCanplay(() => {
    
    
        audioContext.play()
      })
      // -歌曲结束自动播放下一首歌曲 看文档API
      audioContext.onEnded(()=>{
    
    
        this.changeNewSong()
      })

    }
  },

08_(掌握)播放页-播放模式的切换和歌曲的切换

通过索引记录播放模式 playModeIndex

image-20230131222857560

   <image
        class="btn mode" 
        src="/assets/images/player/play_{
    
    {playModeName}}.png"
        bindtap="onModeBtnTap"
      />

const modeNames = ["order", "repeat", "random"]

data:{
    
    
    playModeIndex:0 ,//0.顺序播放,1.单曲循环2.随机播放
    playModeName:"order"
}
  changeNewSong(isNext = true) {
    
    
    // 1.获取之前的数据
    const length = this.data.playSongList.length
    let index = this.data.playSongIndex
    
1    // 2.根据歌曲播放的模式  之前的数据计算最新的索引    
     
    switch(this.data.playModeIndex){
    
    
      case 0://顺序播放
      index = isNext ? index + 1 : index - 1
      if (index === length) index = 0
      if (index === -1) index = length - 1
      break
      case 1://单曲循环
      break
      case 2://随机播放
      index = Math.floor(Math.random()*length)
      break
    }
    // 3.根据索引获取当前的歌曲信息
    const newSong = this.data.play  ongList[index]
    console.log(newSong.id);
    this.setupPlaySong(newSong.id)
    // 4.保存最新的索引值
    playerStore.setState("playSongIndex", index)
  },


onModeBtnTap(){
    
    
    let modeIndex = this.data.playModeIndex
    modeIndex = modeIndex + 1
    if(modeIndex===3) modeIndex
    this.setData({
    
    playModeIndex:modeIndex,playModeName:modeNames[modeIndex]})
}

09_(掌握)播放页-播放逻辑的抽取思想和返回功能

返回功能

nav-bar

  <view class="left" bindtap="onLeftClick">
      
 methods: {
    
    
    onLeftClick() {
    
    
      this.triggerEvent("leftclick")
    }
  }     

music-player

<nav-bar bind:leftclick="onNavBackTap">
 
onNavBackTap(){
    
    
    wx.navigateBack()
}    

10_(掌握)播放页-滑块拖动优化-单曲循环实现方式二

使用节流–滑块拖动优化-(滑块拖动本身调用太频繁-里面setData会刷新整个页面使用性能消耗大)

  // onSliderChangeing事件,滑动滑块松下的时候调用---使用节流
  onSliderChangeing: throttle(function (event) {
    
    
    //1.获取滑块到的位置的valuer
    const value = event.detail.value
    //2.根据当前的值,计算出对应的事件
    const currentTime = value / 100 * this.data.durationTime
    this.setData({
    
    
      currentTime
    })
    //3.变量记录滑块当前正在滑动
    this.data.isSliderChanging = true
  },100),

单曲循环实现方式二单曲循环时点击下一首,播放下一首


  // 模式改变
  onModeBtnTap() {
    
    
    // 1.计算我们新的模式
    let modeIndex = this.data.playModeIndex
    modeIndex = modeIndex + 1
    if (modeIndex === 3) modeIndex
    // 是否是单曲循环
    if(modeIndex === 1){
    
    
      audioContext.loop = true
    }
    else{
    
    
      audioContext.loop = false
    }
    // 2.保存当前的模式
    this.setData({
    
    
      playModeIndex: modeIndex,
      playModeName: modeNames[modeIndex]
    })
  },
      
      
  changeNewSong(isNext = true) {
    
    
    // 1.获取之前的数据
    const length = this.data.playSongList.length
    let index = this.data.playSongIndex
    // 2.根据之前的数据计算最新的索引    
    switch (this.data.playModeIndex) {
    
    
      // swith会进行穿透  case 1:  和 case 0: 都会执行case:0的代码
      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
    }
    // 3.根据索引获取当前的歌曲信息
    const newSong = this.data.playSongList[index]
    console.log(newSong.id);
    this.setupPlaySong(newSong.id)
    // 4.保存最新的索引值
    playerStore.setState("playSongIndex", index)
  },
      
  // -歌曲结束自动播放下一首歌曲 看文档API
      audioContext.onEnded(() => {
    
    
        // 如果是单曲循环,不需要切换下一首格
        if(audioContext.loop) return
        // 切换下一首歌曲
        this.changeNewSong()
      })    

11_(理解)代码重构-播放页的播放逻辑的抽取和重构

播放页的播放逻辑的抽取和重构到新页面

将重构的代码music-player.js放到playerStore.js

1.将data数据划分为页面数据和播放公共的数据

12_(理解)代码重构-播放页监听Store中的数据

13_(理解)代码重构-滑块的交互和播放-暂停按钮的点击

14_(理解)代码重构-播放模式的逻辑抽取封装

15_(理解)代码重构-播放新歌曲的逻辑抽取封装

music-player.js

// pages/music-player/music-player.js
import playerStore, {
    
     audioContext } from "../../store/playerStore"
import {
    
     throttle } from 'underscore'

const app = getApp()
const modeNames = ["order", "repeat", "random"]

Page({
    
    
  data: {
    
    
    stateKeys: ["id", "currentSong", "durationTime", "currentTime", "lyricInfos", "currentLyricText", "currentLyricIndex", "isPlaying", "playModeIndex"],

    id: 0,
    currentSong: {
    
    },
    currentTime: 0,
    durationTime: 0,
    lyricInfos: [],
    currentLyricText: "",
    currentLyricIndex: -1,
    
    isPlaying: true,
    
    playSongIndex: 0,
    playSongList: [],
    isFirstPlay: true,
    
    playModeName: "order",

    pageTitles: ["歌曲", "歌词"],
    currentPage: 0,
    contentHeight: 0,
    sliderValue: 0,
    isSliderChanging: false,
    isWaiting: false,

    lyricScrollTop: 0
  },
  onLoad(options) {
    
    
    // 0.获取设备信息
    this.setData({
    
     
      statusHeight: app.globalData.statusHeight,
      contentHeight: app.globalData.contentHeight
    })

    // 1.获取传入的id
    const id = options.id

    // 2.根据id播放歌曲
    if (id) {
    
    
      // 调用播放歌曲方法
      playerStore.dispatch("playMusicWithSongIdAction", id)
    }

    // 3.获取store共享数据
    // 获取store中的歌曲列表playSongList,playSongIndex
    playerStore.onStates(["playSongList", "playSongIndex"], this.getPlaySongInfosHandler)
    // 播放页面逻辑data数据
    playerStore.onStates(this.data.stateKeys, this.getPlayerInfosHandler)
  },
  // 对currentTime进行节流
  updateProgress: throttle(function(currentTime) {
    
    
    if (this.data.isSliderChanging) return
    // 1.记录当前的时间 2.修改sliderValue
    const sliderValue = currentTime / this.data.durationTime * 100
    this.setData({
    
     currentTime, sliderValue })
  }, 800, {
    
     leading: false, trailing: false }),

  // ==================== 事件监听 ==================== 
  // 页面跳转
  onNavBackTap() {
    
    
    wx.navigateBack()
  },
  // 轮播图页面跳转
  onSwiperChange(event) {
    
    
    this.setData({
    
     currentPage: event.detail.current })
  },
  // 自定义导航栏
  onNavTabItemTap(event) {
    
    
    const index = event.currentTarget.dataset.index
    this.setData({
    
     currentPage: index })
  },
  // 滑块改变调用
  onSliderChange(event) {
    
    
    this.data.isWaiting = true
    setTimeout(() => {
    
    
      this.data.isWaiting = false
    }, 1500)
    // 1.获取点击滑块位置对应的value
    const value = event.detail.value

    // 2.计算出要播放的位置时间
    const currentTime = value / 100 * this.data.durationTime

    // 3.设置播放器, 播放计算出的时间
    audioContext.seek(currentTime / 1000)
    this.setData({
    
     currentTime, isSliderChanging: false, sliderValue: value })
  },
  // 滑块开始改变调用
  // 节流throttle
  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),
  // 歌曲播放记录
  onPlayOrPauseTap() {
    
    
    playerStore.dispatch("changeMusicStatusAction")
  },
  onPrevBtnTap() {
    
    
    playerStore.dispatch("playNewMusicAction", false)
  },
  onNextBtnTap() {
    
    
    playerStore.dispatch("playNewMusicAction")
  },
  onModeBtnTap() {
    
    
    playerStore.dispatch("changePlayModeAction")
  },

  // ====================== store共享数据 ====================
  // 将歌曲列表的数据存到data中
  // 将store里面的数据设置到data里面
  getPlaySongInfosHandler({
     
      playSongList, playSongIndex }) {
    
    
    if (playSongList) {
    
    
      this.setData({
    
     playSongList })
    }
    if (playSongIndex !== undefined) {
    
    
      this.setData({
    
     playSongIndex })
    }
  },
  // 将store的数据存到data中
  // 将store里面的数据设置到data里面
  getPlayerInfosHandler({
     
      
    id, currentSong, durationTime, currentTime,
    lyricInfos, currentLyricText, currentLyricIndex,
    isPlaying, playModeIndex
  }) {
    
    
    if (id !== undefined) {
    
    
      this.setData({
    
     id })
    }
    if (currentSong) {
    
    
      this.setData({
    
     currentSong })
    }
    if (durationTime !== undefined) {
    
    
      this.setData({
    
     durationTime })
    }
    if (currentTime !== undefined) {
    
    
      // 根据当前时间改变进度
      this.updateProgress(currentTime)
    }
    if (lyricInfos) {
    
    
      this.setData({
    
     lyricInfos })
    }
    if (currentLyricText) {
    
    
      this.setData({
    
     currentLyricText })
    }
    if (currentLyricIndex !== undefined) {
    
     
      // 修改lyricScrollTop
      this.setData({
    
     currentLyricIndex, lyricScrollTop: currentLyricIndex * 35 })
    }
    if (isPlaying !== undefined) {
    
    
      this.setData({
    
     isPlaying })
    }
    if (playModeIndex !== undefined) {
    
    
      this.setData({
    
     playModeName: modeNames[playModeIndex] })
    }
  },
  // 卸载 销毁数据
  onUnload() {
    
    
    playerStore.offStates(["playSongList", "playSongIndex"], this.getPlaySongInfosHandler)
    playerStore.offStates(this.data.stateKeys, this.getPlayerInfosHandler)
  }
})

playerStore.js

import {
    
     HYEventStore } from 'hy-event-store'
import {
    
     getSongDetail, getSongLyric } from "../services/player"
import {
    
     parseLyric } from "../utils/parse-lyric"

export const audioContext = wx.createInnerAudioContext()

const playerStore = new HYEventStore({
    
    
  // 
  state: {
    
    
    playSongList: [],
    playSongIndex: 0,

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

    isFirstPlay: true,

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

  actions: {
    
    
    // 播放歌曲的方法
    playMusicWithSongIdAction(ctx, id) {
    
    
      // 0.原来的数据重置
      ctx.currentSong = {
    
    }
      ctx.durationTime = 0
      ctx.durationTime = 0
      ctx.currentLyricIndex = 0
      ctx.currentLyricText = ""
      ctx.lyricInfos = []

      // 1.保存id
      ctx.id = id
      ctx.isPlaying = true

      // 2.请求歌曲相关的数据
      // 2.1.根据id获取歌曲的详情
      getSongDetail(id).then(res => {
    
    
        ctx.currentSong = res.songs[0]
        ctx.durationTime = res.songs[0].dt
      })

      // 2.2.根据id获取歌词的信息
      getSongLyric(id).then(res => {
    
    
        const lrcString = res.lrc.lyric
        const lyricInfos = parseLyric(lrcString)
        ctx.lyricInfos = lyricInfos
      })

      // 3.播放当前的歌曲
      audioContext.stop()
      audioContext.src = `https://music.163.com/song/media/outer/url?id=${
      
      id}.mp3`
      audioContext.autoplay = true

      // 4.监听播放的进度
      if (ctx.isFirstPlay) {
    
    
        ctx.isFirstPlay = false

        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
        })
        audioContext.onWaiting(() => {
    
    
          audioContext.pause()
        })
        audioContext.onCanplay(() => {
    
    
          audioContext.play()
        })
        audioContext.onEnded(() => {
    
    
          // 如果是单曲循环, 不需要切换下一首歌
          if (audioContext.loop) return

          // 切换下一首歌曲
          this.dispatch("playNewMusicAction")
        })
      }
    },
    // 改变音乐状态
    changeMusicStatusAction(ctx) {
    
    
      if (!audioContext.paused) {
    
    
        audioContext.pause()
        ctx.isPlaying = false
      } else {
    
    
        audioContext.play()
        ctx.isPlaying = true
      }
    },
    // 播放类型
    changePlayModeAction(ctx) {
    
    
      // 1.计算新的模式
      let modeIndex = ctx.playModeIndex
      modeIndex = modeIndex + 1
      if (modeIndex === 3) modeIndex = 0

      // 设置是否是单曲循环
      if (modeIndex === 1) {
    
    
        audioContext.loop = true
      } else {
    
    
        audioContext.loop = false
      }

      // 2.保存当前的模式
      ctx.playModeIndex = modeIndex
    },
    // 改变播放的歌曲
    playNewMusicAction(ctx, isNext = true) {
    
    
      // 1.获取之前的数据
      const length = ctx.playSongList.length
      let index = ctx.playSongIndex

      // 2.根据之前的数据计算最新的索引
      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
      }

      // 3.根据索引获取当前歌曲的信息
      const newSong = ctx.playSongList[index]

      // 开始播放新的歌曲
      this.dispatch("playMusicWithSongIdAction", newSong.id)

      // 4.保存最新的索引值
      ctx.playSongIndex = index
    }
  }
})

export default playerStore

16_(理解)首页播放栏-播放栏的整体布局实现

main-music

结构

image-20230201133804176

<!-- 6.播放工具栏 -->
<view class="play-bar-placeholder" wx:if="{
    
    {currentSong.name || isPlaying}}"></view>
<view class="play-bar" wx:if="{
    
    {currentSong.name || isPlaying}}">
  <view class="left">
    <image 
      class="album album-anim" 
      src="{
    
    {currentSong.al.picUrl}}"
      style="animation-play-state: {
    
    { isPlaying? 'running': 'paused' }};"
      bindtap="onPlayBarAlbumTap"
    />
    <view class="name">{
    
    {
    
    currentSong.name}}</view>
  </view>
  <view class="right">
    <image 
      class="play" 
      src="/assets/images/music/{
    
    { isPlaying ? 'pause': 'play' }}_icon.png"
      bindtap="onPlayOrPauseBtnTap"
    />
    <image class="icon" src="/assets/images/music/playlist_icon.png"></image>
  </view>
</view>

样式

/* 播放工具栏 */
.play-bar-placeholder {
    
    
  height: 50px;
}

.play-bar {
    
    
  position: fixed;
  left: 0;
  right: 0;
  height: 44px;
  bottom: 0;
  display: flex;
  justify-content: space-between;
  align-items: center;

  box-sizing: border-box;
  padding: 0 8px;

  box-shadow: 0 -1px 5px rgba(0, 0, 0, 0.1);
  background: #fafafa;
}

.play-bar .left, .play-bar .right {
    
    
  display: flex;
  align-items: center;
}

.play-bar .left .album {
    
    
  position: relative;
  top: -8px;

  width: 44px;
  height: 44px;
  border-radius: 50%;
}

.play-bar .left .name {
    
    
  font-size: 14px;
  margin-left: 10rpx;
}

.play-bar .right .icon {
    
    
  width: 30px;
  height: 30px;
}

.play-bar .right .play {
    
    
  width: 25px;
  height: 25px;
  margin-right: 10rpx;
}

@keyframes rotateAnim {
    
    
  0% {
    
    
    transform: rotate(0);
  }

  100% {
    
    
    transform: rotate(360deg);
  }
}

.album-anim {
    
    
  animation: rotateAnim 10s linear infinite;
}

逻辑

data: {
    
    
// 当前正在播放的歌曲信息
    currentSong: {
    
    },
},
onLoad(){
    
    
    playerStore.onStates(["currentSong"], this.handlePlayInfos)
}    

handlePlayInfos({
     
      currentSong }) {
    
    
    if (currentSong) {
    
    
      this.setData({
    
     currentSong })
    }  
  },       
  onUnload() {
    
    
     playerStore.offStates(["currentSong"], this.handlePlayInfos) 
  }  

17_(理解)首页播放器-播放控制和动画效果实现

data: {
    
    
	// 当前正在播放的歌曲信息
    currentSong: {
    
    },
     //是否正在播放   
     isPlaying: false    
},
onLoad(){
    
    
   playerStore.onStates(["currentSong", "isPlaying"], this.handlePlayInfos)
}    

handlePlayInfos({
     
      currentSong, isPlaying }) {
    
    
    if (currentSong) {
    
    
      this.setData({
    
     currentSong })
    }
    if (isPlaying !== undefined) {
    
    
      this.setData({
    
     isPlaying })
    }
  },
 // 改变音乐状态   
  onPlayOrPauseBtnTap() {
    
    
    playerStore.dispatch("changeMusicStatusAction")
  },    
      
  onUnload() {
    
    
       playerStore.offStates(["currentSong", "isPlaying"], this.handlePlayInfos)
  }  

18_(理解)首页播放器-点击封面进入播放页面

main-music

  <image 
      class="album album-anim" 
      src="{
    
    {currentSong.al.picUrl}}"
      style="animation-play-state: {
    
    { isPlaying? 'running': 'paused' }};"
      bindtap="onPlayBarAlbumTap"
    />
  
  onPlayBarAlbumTap() {
    
    
    wx.navigateTo({
    
    
      url: '/pages/music-player/music-player',
    })
  },        
          

music-player

有值的时候播放歌曲
    // 2.根据id播放歌曲
    if (id) {
    
    
      // 调用播放歌曲方法
      playerStore.dispatch("playMusicWithSongIdAction", id)
    }

19_(掌握)分包处理-文件夹划分和app分包配置

包的整体打包大小

image-20230201160744941

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HlidTfju-1675305754695)(null)]

使用分包

https://developers.weixin.qq.com/miniprogram/dev/framework/subpackages/basic.html

20_(了解)分包处理-独立分包-预下载-异步使用

21_(掌握)手动优化包大小-Vant库优化

手动优化包大小,注意公共的组件依赖不能删,使用的组件不能删,使用组件里面依赖的组件不能删,使用组件里面依赖的组件中的依赖也不能删,递归直至没有依赖的组件,其他手动删除即可

22_(掌握)上传和发布小程序-成员和版本管理

23_(理解)内容回顾和作业布置

第八章:内容回顾

一. 播放页功能

1.1. 歌词分页展示

  • 上下有间距
    • padding
  • 根据当前的歌词进行滚动
    • lyricIndex * 35
  • 选中某一句歌词
    • index === lyricIndex ? ‘active’

1.2. 歌曲列表数据

  • playerStore
    • playSongList
    • playSongIndex

1.3. 上一首/下一首

  • index + 1 / -1
  • 判断边界
  • 获取当前歌曲
  • 记录最新的索引

1.4. 播放模块的切换

  • 0: 顺序播放
  • 1: 单曲循环
  • 2: 随机播放

二. Store代码重构(选做)

2.1. 播放歌曲的代码重构

  • setupPlayMusic()
  • playMusicWidthSongIdAction

2.2. 播放和暂停的功能

2.3. 播放模式的切换

2.4. 播放新歌曲的抽取

三. 首页的play-bar

3.1. 界面搭建

3.2. 交互的操作

3.3. 动画的展示

四. 项目打包和发布

4.1. 分包处理

4.2. 包的大小优化

  • vant

4.3. 项目发布

  • 成员管理
  • 版本管理

猜你喜欢

转载自blog.csdn.net/SOLar7SysteM/article/details/128846110