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

news2024/11/18 5:33:28

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

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. 项目发布

  • 成员管理
  • 版本管理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/195304.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

嘉明的云计算与大数据学习之大数据综合实验案例

1.实验环境 (1)Linux:Ubuntu 16.04。 虚拟机镜像下载链接&#xff1a;https://pan.baidu.com/s/1i_B-2rAfPM53jf7Besi0tw 提取码&#xff1a;WZJM (2)MySQL:5.7.16。 (3)Hadoop:2.7.1。 (4)HBase:1.1.5。 (5)Hive:1.2.1。 (6)Sqoop:1.4.6。 (7)R:4.1.2。 (8)Eclipse:3.8。 2.…

Docklight Scripting模拟串行端口

Docklight Scripting模拟串行端口 Docklight是测试、分析和模拟串行端口(RS232、RS485/422等)的工具。此程序允许您监视和测试两个串行端口之间的连接。该程序环境简单&#xff0c;安装在所有Windows XP/7/8/10版本上。Docklight Scripting是一种带有内部编辑器的脚本语言&…

电子会议桌牌——网络版

产品特征&#xff1a; 低功耗&#xff0c;常规使用3-5年电池寿命支持空中唤醒&#xff0c;刷新快速&#xff0c;几秒钟内看到结果点阵电子纸屏幕&#xff0c;视角接近180基于Web的应用界面&#xff0c;支持跨平台操作安装简单&#xff0c;快速布置电池供电不需要布线双面显示…

Python你绝对不知道的15个小技巧,知道的算我输

名字&#xff1a;阿玥的小东东 学习&#xff1a;Python语言、正在学习c语言 主页&#xff1a;阿玥的小东东 目录 前言 1.all or any 2.bashplotlib 3.collections 4.dir 5.from __future__ import 6.python的优点 7.geopy 8.python2.x中的编码不是 unicode &#xff…

转行自学Java没后悔,我的经历证明,改变永远没有错

我在北京工作6年&#xff0c;现在年薪42万&#xff08;仅代表个人收益&#xff09;&#xff0c;目前在搜狐畅游工作&#xff0c;参与过轩辕剑的项目研发&#xff0c;有几个大型项目实战的经历&#xff0c;不过最后这几款项目都失败了。我之所以选择当初学习Java&#xff0c;就一…

我靠CSGO搬砖项目,一个月净赚过万

作为一名四线小城市的平凡上班族&#xff0c;今年已经满30岁了&#xff0c;工资到手也才6000多&#xff0c;已经不记得有多少年没涨过了。 后来通过CSGO搬运项目&#xff0c;每月副业收益稳定在5万的收益。 废话不多说&#xff0c;先给大家瞅瞅我上个月带货的收益&#xff0c…

为什么要上机械设备ERP系统?对企业管理有什么帮助?

机械设备制造行业是国民经济增长的重要支撑&#xff0c;随着信息化的发展&#xff0c;机械设备制造企业面临各种各样的挑战&#xff0c;频繁地出现部门监管力度不够、生产计划难定、产品质量不过关、生产过程操作不规范以及原材料使用不当等&#xff0c;造成资源浪费的现象&…

规则引擎设计与实现

规则引擎设计与实现 「字节跳动第五届青训营」笔记 是什么 规则引擎是一种嵌入在应用程序中的组件&#xff0c;实现了将业务决策从应用程序代码中分离出来&#xff0c;并 使用预定义的语义模块编写业务决策。接受数据输入&#xff0c;解释业务规则&#xff0c;并根据业务规则…

用最简单的方法实现个人网站-wordpress结合LAMP架构实战

文章目录前言一、LAMP架构是什么&#xff1f;1.1 LAMP架构简介1.2 什么是apache?1.3 什么是MySQL&#xff1f;1.4 什么是php&#xff1f;二. wordpress是什么三. 搭建博客过程1. 实验环境2. 搭建LAMP架构3. 设置数据库并授权4. 上传wordpress并解压5. 修改配置文件6. 网页端进…

基于C#制作一个网址检测工具

此文主要基于C#制作一个网址检测工具。随着时间推移以及公司人员的变动&#xff0c;手头所负责的项目也越来越多&#xff0c;对网站运行状况的管理也是一件让人十分头疼的事&#xff0c;秉持着偷懒是第一生产力的原则&#xff0c;制作一个工具对网址一键监测。 实现流程1、创建…

支付测试怎么做?要点有哪些?

目录 支付测试 避不开的 公司有没有支付牌照的问题&#xff1f; 国内有支付牌照的公司&#xff08;搞一张支付牌照 &#xff0c;不是钱多少的事&#xff09;总共 200 多家而已​编辑 没有支付牌照 公司就不可以支付么&#xff1f; 没有支付牌照 的公司的支付 做的什么支付&…

文档存储Elasticsearch系列--1 ES介绍

前言&#xff1a;Elasticsearch 也是使用 Java 编写的&#xff0c;它的内部使用 Lucene 做索引与搜索&#xff0c;支持结构化文档数据的分布式存储&#xff0c;并提供准实时的查询&#xff0c;全文检索&#xff0c;数据聚合&#xff1b; 1 为什么要使用ES: ES 本身存在哪些特性…

第二章---进程的描述与控制

&#x1f31e;欢迎来到操作系统的世界 &#x1f308;博客主页&#xff1a;卿云阁 &#x1f48c;欢迎关注&#x1f389;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f31f;本文由卿云阁原创&#xff01; &#x1f64f;作者水平很有限&#xff0c;如果发现错误&#xff…

打印机之——G3800故障维修

一 故障现象 5B00/1471/1472打印机进空气二 解决办法 2.1 5B00/1471/1472 解决办法——不需要软件清零,按键解决 步骤1 插上电源&#xff0c;打印机处于未开机状态&#xff0c;挡板处放入打印纸同时按住“停止”键和“电源”键&#xff0c;此时“电源”键亮起松开“停止”键…

python-opencv第六期:addWeighted函数详解

概要&#xff1a; 众嗦粥汁所周知&#xff0c;在如今计算机视觉&#xff08;Computer Version short for CV&#xff09;是人工智能与机器人技术发展的一个重大研究方向&#xff0c;而opencv作为一个专门为机器视觉编程提供技术与函数支持的第三方库&#xff0c;自然是一个需要…

Devops简介

Devops DevOps是一组过程、方法与系统的统称&#xff0c;用于促进开发、技术运营和**质量保障&#xff08;QA&#xff09;**部门之间的沟通、协作与整合。 其目的就是让开发人员和运维人员更好的沟通合作的文化、运动或惯例&#xff0c;通过自动化交付流程来使软件开发&#x…

牛客网C基础题目练习1

目录题目1&#xff1a;竞选社长题目2&#xff1a;最高分数题目3&#xff1a;判断是元音还是辅音题目4&#xff1a;判断是不是字母题目5&#xff1a;字母大小写转换题目1&#xff1a;竞选社长&#x1f4ad;法1&#xff1a;使用字符串数组代码实现&#xff1a;int main() {char a…

【按位考虑DP】代码源每日一题div1 2023.02.02 与

与 - 题目 - Daimayuan Online Judge这太酷了&#xff0c;很符合我对DP的想象这DP太妙了&#xff0c;我还没做过这种按位考虑DP的题题意&#xff1a;思路&#xff1a;首先看第二个条件&#xff0c;一个数与后面的那个数与了之后还是后面那个数&#xff0c;这说明什么我们按位考…

快速入门KMM和Compose Multiplatform

一、前言 最近才有空&#xff0c;想起来Compose-jb和kmm这2个框架&#xff0c;就来个快速入门指南吧。 什么是KMM (Kotlin Multiplatform Mobile) KMM用于简化跨平台开发&#xff0c;可以在Android和IOS之间共享通用的代码。 仅在使用各自平台能力的时候&#xff0c;才会去编…

解决前端组件渲染没更新数据问题

问题&#xff1a; 使用前端日期控件时 - 数据联动时数据绑定无效问题 现象&#xff1a; 选择A日期&#xff0c;想动态改变B日期数据&#xff0c;只有第一次选择时会动态改变B日期数据&#xff0c;第二次选择A日期时&#xff0c;B日期数据虽已改变&#xff0c;但是页面数据未改变…