h5音乐播放器,自定义外观。(纯原生)

 一,首先介绍一下h5原生播放器的属性和事件。

属性(这里只介绍本文使用到的属性和常用属性):

​​​autoplay​​​​​​​     是否自动播放

controls     是否隐藏控制面板

currentTime    当前播放进度以秒为单位

duration         进度条总长度

loop           是否循环播放

muted       是否静音

preload      资源加载方式 ;   none表示不进行预加载,metadata表示不进行预加载但会获取进度长度,auto表示对资源进行预加载

src           资源加载路径

事件(这里只介绍本文使用到的事件和常用事件):

事件名称触发时机
ended播放到媒体的结束位置,播放停止。
loadeddata媒体的第一帧加载完成。
loadedmetadata元数据加载完成。
pause播放暂停。
play播放开始。
playing因为缺少数据而暂停或延迟的状态结束,播放准备开始。
ratechange播放速度变化。
timeupdate由 currentTime 指定的时间更新。
volumechange音量变化。
waiting因为暂时性缺少数据,播放暂停。

二,实现思路讲解

   场景:客户需要一个可以变更样式的简单音乐播放控件。进度条可拖拽,可暂停播放,可调整音量。(没有一键静音😂,感觉不是很难可以自己加)

   解决方案:使用h5原生播放器<audio>原生功能并隐藏原生控制面板,通过html标签重构外观实现。

   实现效果:如下

三,使用到的技术方案展示

        js/html/scss/vue2/elementui

选型思路,首先三件套是基础,vue2主要是因为公司的项目比较老旧,所以没有选择最新的vue3框架,后期会考虑往vue3迁徙。最后用到elemtui。主要是因为手写一个进度条太浪费时间,而且实现进度条的技术难度很低,自己实现一个进度条可以说是既浪费时间又没有什么提升。所以进度条方案选择了elementui组件。

直接上完整源码

<template>
  <div class="audioPlayer" v-show="visible">
    <div class="audioTitle">
      {{ title }}
    </div>
    <div class="btn">
      <i
        @click="playandstop"
        :class="!isplay ? 'el-icon-video-play' : 'el-icon-video-pause'"
      ></i>
    </div>
    <div class="progressBox">
      <div style="margin-right: 20px">{{ start }}</div>
      <el-slider
        v-model.lazy="sliderValue"
        style="width: 400px"
        @input="clickSilder"
        @change="clickSilder"
        :show-tooltip="false"
        :max="maxTime"
        :step="0.1"
      ></el-slider>
      <div style="margin-left: 15px">{{ end }}</div>
      <div>
        <div class="volumeBox">
          <div class="volumeSlider">
            <span>{{ volumeValue }}</span>
            <el-slider
              v-model="volumeValue"
              vertical
              height="100px"
              @change="volumeSilder"
              :max="maxVolume"
              :min="0"
              :show-tooltip="false"
            >
            </el-slider>
          </div>
          <div class="imgWaper">
            <img src="@/assets/images/workSquare/volumeBlock.png" alt="" />
          </div>
        </div>
      </div>
    </div>
    <div class="close" v-if="showCloseBtn">
      <i class="el-icon-close" @click="$emit('update:visible', false)"></i>
    </div>
    <audio
      @volumechange="volumeChange"
      @ended="playEnded"
      @loadedmetadata="loadedmetadata"
      style="display: none"
      id="audioPlayer"
      :src="audioUrl"
      preload="auto"
      :autoplay="autoplay"
    ></audio>
  </div>
</template>

<script>
export default {
  props: {
    visible: {
      type: Boolean,
      default: true
    },
    audioUrl: {
      type: String,
      default: ''
    },
    title: {
      type: String,
      default: ''
    },
    showCloseBtn: {
      type: Boolean,
      default: false
    },
    autoplay: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      isplay: false,
      start: '0:00', //初始值
      end: '0:00', //初始值
      audio: null, //audio元素
      sliderValue: 0,
      maxTime: 100,
      inThrottle: undefined,
      volumeValue: 100,
      maxVolume: 100
    }
  },
  mounted() {
    this.audio = document.querySelector('#audioPlayer')
    this.audio.addEventListener('timeupdate', this.domDone)
  },
  methods: {
    formate(time) {
      const minutes = Math.floor(time / 60)
      const remainingSeconds = Math.floor(time % 60)
      // 确保秒数是两位数
      const displaySeconds =
        remainingSeconds < 10 ? '0' + remainingSeconds : remainingSeconds
      return minutes + ':' + displaySeconds
    },
    domDone(event) {
      this.$nextTick(() => {
        this.start = this.formate(event.target.currentTime)
        this.end = event.target.duration
          ? this.formate(event.target.duration)
          : ''
        this.sliderValue = Math.floor(event.target.currentTime * 10) / 10
        this.maxTime = Math.floor(event.target.duration * 10) / 10
      })
    },
    playandstop() {
      if (this.audio.paused) {
        this.audio.play()
        this.isplay = true
      } else {
        this.audio.pause()
        this.isplay = false
      }
    },
    volumeChange() {
      this.volumeValue = Math.round(this.audio.volume * 100)
    },
    clickSilder(val) {
      // 优化拖动silder卡顿
      if (!this.inThrottle) {
        this.inThrottle = true
        setTimeout(() => {
          this.inThrottle = false
        }, 50)
      } else {
        this.audio.currentTime = val
        return
      }
    },
    playEnded() {
      this.$emit('playEnded')
    },
    play() {
      this.$nextTick(() => {
        this.audio.play()
      })
    },
    pause() {
      this.$nextTick(() => {
        this.audio.pause()
      })
    },
    reset() {
      // 重置播放器的状态
      this.audio.currentTime = 0
      this.audio.volume = 1
      this.volumeValue = 100
      this.isplay = false
      this.audio.pause()
    },
    loadedmetadata(event) {
      this.start = this.formate(event.target.currentTime)
      this.end = this.formate(event.target.duration)
    },
    volumeSilder(val) {
      this.audio.volume = val / 100
    }
  }
}
</script>

<style scoped lang="scss">
.audioPlayer {
  // 定位
  // position: fixed;
  display: flex;
  justify-content: center;
  align-items: center;
  // bottom: 0px;
  // left: 0px;
  width: 100%;
  height: 50px;
  z-index: 999;
  background: rgba(255, 255, 255, 0.425);
  backdrop-filter: blur(10px);
  border-radius: 5px;
}
.audioTitle {
  margin-right: 10px;
}
.progressBox {
  display: flex;
  align-items: center;
}
.btn {
  display: flex;
  align-items: center;
  cursor: pointer;
  i {
    scale: 1.5;
    margin-right: 10px;
    display: flex;
    align-items: center;
    color: rgb(29, 29, 29);
  }
}
.close {
  position: absolute;
  top: 5px;
  right: 5px;
  cursor: pointer;
}
.volumeBox {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  cursor: pointer;
  height: 50px;
  .volumeSlider {
    position: absolute;
    left: 0px;
    top: -118px;
    padding-bottom: 10px;
    display: none;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    background: rgba(255, 255, 255, 0.911);
    backdrop-filter: blur(10px);
    border-radius: 5px;
    > span {
      display: flex;
      width: 100%;
      justify-content: center;
      margin-bottom: 10px;
      color: #000000;
    }
    ::v-deep .el-slider__button {
      width: 7px;
      height: 7px;
    }
    ::v-deep .el-slider.is-vertical .el-slider__runway {
      width: 5px;
    }
    .imgWaper {
      height: 100%;
    }
  }
  img {
    height: 20px;
    width: 20px;
    margin-left: 8px;
    margin-top: 5px;
  }
}
.volumeBox:hover {
  .volumeSlider {
    display: flex !important;
  }
}
::v-deep .el-slider__button {
  width: 10px;
  height: 10px;
}
</style>

这个音乐播放器是基于简单需求进行开发的,后期应该不会再升级更复杂的功能,此播放器的开发被我暂停在了1.0初级版本。

如果各位需要更复杂的功能和需求可以再此基础上进行升级开发,可拓展的空间很大。

如果点击量不错,后续还会更新

    h5原生视频播放器自定义播放器外观

敬请期待

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值