在music-list.vue中点击歌曲列表,进入播放器页面
图片描述
store/actions.js中定义action,提交到mutation

//选择播放
export const selectPlay = function({commit, state}, {list, index}) {
  commit(types.SET_SEQUENCE_LIST, list)
  commit(types.SET_PLAYLIST, list)           //播放列表
  commit(types.SET_CURRENT_INDEX, index)     //当前播放歌曲的索引
  commit(types.SET_FULL_SCREEN, true)        //全屏
  commit(types.SET_PLAYING_STATE, true)      //播放状态
}

图片描述
在player.vue组件中,获取playlist所以组件v-show=true

...mapGetters{[
    'playlist'
]}

1.audio

  • playing:音频或视频已开始播放时触发
  • error:在音频或视频加载发生错误时触发
  • timeupdate:播放位置改变时触发[注意:播放和调整指示定位时都会触发]
  • ended:音频或视频文件播放完毕后触发
 <audio ref="audio" @playing="ready" @error="error" @timepudate="updateTime"
    @ended="end"></audio>

2 vuex中获取数据

...mapGetters([
      'fullScreen',   
      'playlist',
      'currentSong',
      'playing',
      'currentIndex',
      'mode',
      'sequenceList'
    ])

3 在watch中定义currentSong playing

在点击歌曲列表之后,currentSong playing都有了相应的数据

  • 获取相应的歌曲
  • 获取歌词
watch: {
    //在computed的getter中获取currentSong
    currentSong(newSong, oldSong) {
      if (!newSong.id || !newSong.url || newSong.id === oldSong.id) {
        return
      }
      this.songReady = false
      this.canLyricPlay = false
      //获取歌词
      if (this.currentLyric) {
        //切换歌曲的时候,清除上一个歌曲的定时器,歌词跳动的bug
        this.currentLyric.stop()
        // 同时,将相关重置为null
        this.currentLyric = null
        this.currentTime = 0
        this.playingLyric = ''
        this.currentLineNum = 0
      }
      //播放歌曲
      this.$refs.audio.src = newSong.url
      this.$refs.audio.play()

      //歌曲快速切换的时候,先清除定时器
      clearTimeout(this.timer)
      //若歌曲5s未播放,则认为超时,修改状态,确保可以切换歌曲
      this.timer = setTimeout(() => {
        this.songReady = true
      }, 5000)
      this.getLyric()
  },
     playing(newPlaying) {
      if (!this.songReady) {
        return
      }
      const audio = this.$refs.audio
      this.$nextTick(function() {
        newPlaying ? audio.play() : audio.pause()
      })
    },
    fullScreen(newVal) {
      if (newVal) {
        setTimeout(() => {
          this.$refs.lyricList.refresh()
          this.$refs.progressBar.setProgressOffset(this.percent)
        })
      }
    }
}

4.播放器功能按钮

<div class="operators">
    <div class="icon i-left">
      <i @click="changeMode" :class="iconMode"></i>     //播放模式
    </div>
    <div class="icon i-left" :class="disableCls">       //前一首
      <i @click="prev" class="icon-prev"></i>
    </div>
    <div class="icon i-center" :class="disableCls">     //播放/暂停按钮
      <i @click="togglePlaying" :class="playIcon"></i>
    </div>
    <div class="icon i-right" :class="disableCls">      //下一首
      <i @click="next" class="icon-next"></i>
    </div>
</div>

4.1 样式

通过计算属性选择按钮的样式

  computed: {
    playIcon() {   //播放/暂停按钮
      return this.playing ? 'icon-pause' : 'icon-play'
    },
    disableCls() { //音乐是否加载完毕,加载完毕方可播放或者下一首或者上一首
      return this.songReady ? '' : 'disable'
    },
    iconMode() {  //播放模式
      return this.mode === playMode.sequence ? 'icon-sequence' : this.mode === playMode.loop ? 'icon-loop' : 'icon-random'
    },

判断歌曲加载状态audio相关状态

<audio ref="audio" @playing="ready" @error="error" @timepudate="updateTime"
    @ended="end"></audio>
    ready() {
      clearTimeout(this.timer)
      //监听audio的playing状态,可以播放的时候this.songReady=true,
      //这个时候可以点击播放,下一首,上一首,快速切换歌曲
      this.songReady = true
      this.canLyricPlay = true
      //保存播放历史
      this.savePlayHistory(this.currentSong)

      //如果歌曲的播放时间晚于歌词的出现,播放的时候需要同步歌词
      if (this.currentLyric && !this.isPureMusic) {
        this.currentLyric.seek(this.currentTime * 1000)
      }
    },
    error() {
      clearTimeout(this.timer)
      //如果出现网络错误,或者地址不正确,那么this.songReady=false,不能进入ready(),所以不能
      //点击播放,下一首,上一首,所以,在错误的时候,也应该this.songReady = true
      this.songReady = true
    },
    //播放位置改变时触发[注意:播放和调整指示定位时都会触发]
    updateTime(e) {
    //歌曲播放时间进度
      this.currentTime = e.target.currentTime
    },
    //歌曲播放完毕
    end() {
      this.currentTime = 0
      if (this.mode === playMode.loop) {
      //随机播放模式
        this.loop()
      } else {
      
        this.next()
      }
    },

4.2 上一首方法

prev() {
//判断歌曲是否加载完毕
  if (!this.songReady) {
    return
  }

  if (this.playlist.length === 1) {
    //处理边界情况:只有一首歌曲,循环播放
    this.loop()
    //return:因为后面this.songReady = false,会导致播放器按钮不可用
    return
  } else {
    let index = this.currentIndex - 1
    //点击上一首歌曲,到了第一首歌曲的时候,再点击上一首歌曲,则播放最后一首歌曲
    if (index === -1) {
      index = this.playlist.length - 1
    }
    //mutation改变状态currentIndex
    this.setCurrentIndex(index)
    
    //切换上一首歌曲后,如果未播放,则播放
    if (!this.playing) {
      this.togglePlaying()
    }
  }
  this.songReady = false
},

4.3 下一首方法

next() {
  //判断歌曲是否加载完毕
  if (!this.songReady) {
    return
  }
  if (this.playlist.length === 1) {
    //处理边界情况:只有一首歌曲,循环播放
    this.loop()
    //return:因为后面this.songReady = false,会导致播放器按钮不可用
    return
  } else {
    //点击下一首的时候,到了最后一首歌曲的时候,再点击,则播放第一首一首歌曲
    let index = this.currentIndex + 1
    if (index === this.playlist.length) {
      index = 0
    }
    //mutation改变状态currentIndex
    this.setCurrentIndex(index)
    if (!this.playing) {
    //如果未播放,则开始播放
      this.togglePlaying()
    }
  }
  this.songReady = false
},

4.4 播放模式:随机 循环 顺序

changeMode() {
  //common/config.js中  默认是0:顺序    1:loop  2:random
  const mode = (this.mode + 1) % 3
  //同时修改vuex中的播放模式mode
  this.setPlayMode(mode)

  let list = null
  if (mode === playMode.random) {
    //随机播放,洗牌顺序列表
    list = shuffle(this.sequenceList)
  } else {
    //非随机播放,则是顺序播放列表
    list = this.sequenceList
  }
  //切换播放模式后,当前播放的歌曲索引可能发生变化,这个时候重置currentIndex
  this._resetCurrentIndex(list)
  //切换播放模式后  调用mutation更改播放列表
  this.setPlayList(list)
},
_resetCurrentIndex(list) {
  let index = list.findIndex((item) => {
    return item.id === this.currentSong.id
  })
  //重置currentIndex
  this.setCurrentIndex(index)
},

4.5 循环播放

//播放模式:循环播放
loop() {
//重置播放时间:0
  this.$refs.audio.currentTime = 0
//开始播放
  this.$refs.audio.play()
//vuex提交播放状态
  this.setPlayingState(true)
  if (this.currentLyric) {
    //单曲循环的时候,歌词循环播放
    this.currentLyric.seek(0)
  }
},

4.6播放/暂停:按钮

togglePlaying() {
  //如果未加载完毕,则不播放
  if (!this.songReady) {
    return
  }
  //vuex提交播放器播放状态
  this.setPlayingState(!this.playing)
  if (this.currentLyric) {
    //歌曲暂停时,歌词暂停播放
    this.currentLyric.togglePlay()
  }
},

云深不知处
431 声望10 粉丝