VUE移动端音乐APP学习【十三】:播放器progress-bar进度条组件

  • 有了歌曲的当前播放时长和总时长可以算出进度条
  • 组件进度条dom结构
<template>
  <div class="progress-bar" ref="progressBar">
    <div class="bar-inner">
      <div class="progress" ref="progress"></div>
      <div class="progress-btn-wrapper" ref="progressBtn">
        <div class="progress-btn"></div>
      </div>
    </div>
  </div>
</template>

<style lang="scss" scoped>
.progress-bar {
  height: 30px;

  .bar-inner {
    position: relative;
    top: 13px;
    height: 5px;
    background: rgba(0, 0, 0, 0.3);

    .progress {
      position: absolute;
      height: 100%;
      background: $color-theme;
    }

    .progress-btn-wrapper {
      position: absolute;
      left: -8px;
      top: -13px;
      width: 30px;
      height: 30px;

      .progress-btn {
        position: relative;
        top: 7px;
        left: 7px;
        box-sizing: border-box;
        width: 16px;
        height: 16px;
        border: 3px solid $color-text;
        border-radius: 50%;
        background: $color-theme;
      }
    }
  }
}
</style>
  • 在player.vue中引入并注册组件progress-bar
<div class="progress-bar-wrapper" >
        <progress-bar></progress-bar>
</div>

import ProgressBar from '../../base/progress-bar/progress-bar';

components: {
    ProgressBar,
  },
  • 改变进度条的位置  
  1. 先设置进度条百分比的属性,进度条接收一个props,还要添加一个watch,关注percent的变化
  2. 获取整个进度条的实际宽度以及进度条小球的宽度然后进行相减得到进度条总宽度,这里可定义一个常量为小球按钮的宽度
  3. 将歌曲播放的比例newPercent乘以总宽度,可以得到应该偏移的宽度
  4. player组件要计算这个newPercent,将percent传到progress-bar组件

 progress-bar.vue

const progressBtnWidth = 16;

props: {
    percent: {
      type: Number,
      default: 0,
    },
  },
  watch: {
    percent(newPercent) {
      if (newPercent >= 0) {
        const barWidth = this.$refs.progressBar.clientWidth - progressBtnWidth;
        const offsetWidth = newPercent * barWidth;
        this.$refs.progress.style.width = `${offsetWidth}px`;
      }
    },
  },

 player.vue

<div class="progress-bar-wrapper" >
       <progress-bar :percent="percent"></progress-bar>
</div>

computed:{
  ...
percent() {
      return this.currentTime / this.currentSong.duration;
    },  

}
  • 以上运行效果

  • 可以看到功能还没有完善,还需要获取小球的偏移,这个偏移用transform来做
import { prefixStyle } from '../../common/js/dom.js';

const transform = prefixStyle('transform');

 percent(newPercent) {
      if (newPercent >= 0) {
        const barWidth = this.$refs.progressBar.clientWidth - progressBtnWidth;
        const offsetWidth = newPercent * barWidth;
        this.$refs.progress.style.width = `${offsetWidth}px`;
        this.$refs.progressBtn.style[transform] = `translate3d(${offsetWidth}px,0,0)`;
      }
    },

可以看到小球也跟着进度条同步偏移

  •  在进度条组件基础上添加交互操作:

①拖动进度条到相应的位置

1.在progress-btn-wrapper层上添加几个touch事件并且去阻止浏览器的默认行为

<div class="progress-btn-wrapper" ref="progressBtn" 
    @touchstart.prevent
="progressTouchStart" @touchmove.prevent="progressTouchMove" @touchend="progressTouchEnd">

2.需要创建一个实例touch对象来去维护在不同回调之间touch数据的通信

 created() {
    // 这个touch的作用用于在不同的回调里面需要共享一些数据的时候,把这些共享数据挂载到这个对象上
    this.touch = {};
  },

3.当touchStart触发的时候,给这个touch定个标志位叫initiated并设置为true,还要记录touch点击时候的位置以及已经滚动的进度条宽度

    progressTouchStart(e) {
      this.touch.initiated = true;
      // e.touches[0]表示第一个手指
      this.touch.startX = e.touches[0].pageX;
      this.touch.left = this.$refs.progress.clientWidth;
    },

4.定义progressTouchMove方法:重新设置偏移量

    progressTouchMove(e) {
      // 如果没有初始化就进入touchMove事件就return
      if (!this.touch.initiated) {
        return;
      }
      // 当前点击的位置和在touchStart的位置计算出在纵向的偏移量
      const deltaX = e.touches[0].pageX - this.touch.startX;
      const offsetWidth = Math.min(this.$refs.progressBar.clientWidth - progressBtnWidth, Math.max(0, this.touch.left + deltaX));
      this._offset(offsetWidth);
    },

    _offset(offsetWidth) {
    //代码重复 封装为该函数,减少代码重复率
      this.$refs.progress.style.width = `${offsetWidth}px`;
      this.$refs.progressBtn.style[transform] = `translate3d(${offsetWidth}px,0,0)`;
    },

5.在progressTouchEnd方法中将touch的标志位重置为false

    progressTouchEnd() {
      // 重置为false
      this.touch.initiated = false;
    },

6.运行效果:拖动的时候,进度条不断往回跳。因为歌曲正在播放,percent在不断改变,watch percent的时候又修改了进度条的宽度,所以会有冲突。我们需要在拖动的时候不被干扰

解决方法:

在watch的时候加个判断条件需要newPercent>=0并且在没有拖动的过程中才可以改变这个宽度。

percent(newPercent) {
      if (newPercent >= 0 && !this.touch.initiated) {
        const barWidth = this.$refs.progressBar.clientWidth - progressBtnWidth;
        const offsetWidth = newPercent * barWidth;
        this._offset(offsetWidth);
      }
    },

拖完以后需要去告诉外层的播放器拖动到多少,把播放器的进度设置成和拖动的位置一样。

①拖动结束后派发事件,即在progressTouchEnd中调用一个方法去派发事件

    progressTouchEnd() {
      // 重置为false
      this.touch.initiated = false;
      this._triggerPercent();
    },

②定义这个方法:首先获取barWidth以及当前的percent,然后派发事件percentChange,传递参数percent

_triggerPercent() {
      const barWidth = this.$refs.progressBar.clientWidth - progressBtnWidth;
      const percent = this.$refs.progress.clientWidth / barWidth;
      // 派发当前的percent
      this.$emit('percentChange', percent);
    },

③在player监听这个percentChange事件,定义这个事件方法:要改变audio的currentTime才能真正改变播放器的进度条,这个currentTime是可读写属性

 

 <progress-bar :percent="percent" @percentChange="onProgressBarChange"></progress-bar>



onProgressBarChange(percent) {
      this.$refs.audio.currentTime = this.currentSong.duration * percent;
      if (!this.playing) {
        this.togglePlaying();
      }
    },

②点击相应的地方进度条也跟着改变

在progressBar添加点击事件

<div class="progress-bar" ref="progressBar" @click="progressClick">

定义该事件:设置偏移量,可由e.offsetX获得,然后调用_triggerPercent方法派发事件

    progressClick(e) {
      // 设置偏移量
      this._offset(e.offsetX);
      // 去通知外层改变了多少percent
      this._triggerPercent();
    },
  • 运行效果

posted @ 2021-04-25 15:13  小风车吱呀转  阅读(557)  评论(0编辑  收藏  举报