自定义audio播放器

161 阅读2分钟

audio标签 用element-plus中的el-progress模拟播放进度 可直接拿来用 播放器相关事件js
老版本显示兼容: 音量滑道不显示样式兼容问题可加input[type="range"] { writing-mode: vertical-lr; direction: rtl; -webkit-appearance: slider-vertical; appearance: slider-vertical; }处理

<script lang="ts" setup>
import { computed, ref, onMounted, onBeforeUnmount } from 'vue';
import { useCounterStore } from '@/stores'
const userStore = useCounterStore();

const audioPlayer:any = ref(null);
const mp3List:any = [{name:'aa',src:'https://lw-sycdn.kuwo.cn/504881085d1eca4eb48b3d7f3c596992/66f8f7b4/resource/30106/trackmedia/M500002QkflO1okifA.mp3'},
  {name:'bb',src:'https://webfs.kugou.com/202409291521/0db24266f14d9619da72163ac1ebdca9/v3/3bc78d2da5775382b4858da64872de7c/yp/full/ap1014_us0_mii0w1iw8z2ai2iphcu80ooo2ki81120_pi406_mx676479208_s3837368345.mp3'},
  {name:'cc',src:'https://webfs.kugou.com/202409291413/24baa92e27a507f66b28e126f88b0b20/v3/3fb35373b7b200c05679cab6d345d0eb/yp/full/ap1014_us0_mii0w1iw8z2ai2iphcu80ooo2ki81120_pi406_mx676426518_s3681119118.mp3'}]
const volume = ref(50); // 初始音量
const progress = ref(0);
const progressTwo = ref(0);
const duration = ref(0);
const isPlaying = ref(false);
let nextUpdateTime = 0;
let progressUpdateTimer:any = null;
const myDiv:any = ref(null);
const mp3Index = ref(0);
const mp3Name = ref();
userStore.myDiv = myDiv;

// 播放音乐
function play() {
  if (audioPlayer.value) {
    if (mp3List.length > 0) {
      mp3Index.value++;
      audioPlayer.value.src = mp3List[0].src;
      mp3Name.value = mp3List.shift().name;
      isPlaying.value = true;
      audioPlayer.value.play();
      progressUpdateTimer = setInterval(() => {
        updateProgress();
      }, 1000);
    } else {
      stop();
    }
    
  }
}

// 暂停音乐
function pause() {
  if (audioPlayer.value) {
    isPlaying.value = false;
    audioPlayer.value.pause();
  }
}

function vlume() {
  userStore.rangeShow = true;
}
function setVolume(event: Event) {
  if (audioPlayer.value) {
    audioPlayer.value.volume = volume.value / 100;
  }
}

// 更新播放时间
function updateProgress() {
  if (audioPlayer.value) {
    progress.value = Math.floor(audioPlayer.value.currentTime);
    const currentTime = Math.floor(audioPlayer.value.currentTime);
    const durationTime = Math.floor(audioPlayer.value.duration);
    if (currentTime >= nextUpdateTime) {
      progressTwo.value = (currentTime / durationTime) * 100;
      nextUpdateTime += 1; // 假设每秒更新一次
    }
  }
}

// 更新歌曲总时长
function updateDuration() {
  if (audioPlayer.value) {
    duration.value = Math.floor(audioPlayer.value.duration);
  }
}

// 格式化播放时间
const currentTime = computed(() => {
  return formatTime(progress.value);
});

// 格式化总时长
const durationTime = computed(() => {
  return formatTime(duration.value);
});

// 格式化时间为 mm:ss 格式
function formatTime(seconds: number) {
  const minutes = Math.floor(seconds / 60);
  const secondsRemainder = Math.floor(seconds) % 60;
  return `${minutes.toString().padStart(2, '0')}:${secondsRemainder.toString().padStart(2, '0')}`;
}
// 处理音频播放结束的函数
function handleAudioEnd() {
  if(mp3List.length > 0 ){
    isPlaying.value = false;
    progressTwo.value = 0;
    audioPlayer.value.currentTime = 0; // 重置播放时间
    progress.value = 0;
    nextUpdateTime = 0;
    progressTwo.value = 0; // 重置进度条
    clearInterval(progressUpdateTimer);
    // 这里可以执行其他操作,比如自动播放下一首音乐或者显示播放结束的信息
    play()
  }
}

// 停止音乐并重置
function stop() {
  if (audioPlayer.value) {
    isPlaying.value = false;
    audioPlayer.value.pause();
    audioPlayer.value.currentTime = 0; // 重置播放时间
    progress.value = 0; // 重置进度条
    nextUpdateTime = 0;
    progressTwo.value = 0; // 重置进度条
    clearInterval(progressUpdateTimer.value); // 清除定时器
  }
}

onMounted(() => {
  progressUpdateTimer = setInterval(() => {
    updateProgress();
  }, 1000); // 每秒更新一次
});

onBeforeUnmount(() => {
  clearInterval(progressUpdateTimer);
});
</script>

vue3

<template>
  <div class="musicPlayer">
    <!-- @timeupdate="updateProgress" -->
    <audio ref="audioPlayer" @timeupdate="updateProgress" @loadedmetadata="updateDuration" @ended="handleAudioEnd"></audio>
    <img class="playBtn pointer" v-if="isPlaying == false ||  currentTime == durationTime"  @click="play" src="@/assets/images/Latent/playBtn.png" alt="">
    <img class="pauseBtn pointer" v-else @click="pause" src="@/assets/images/Latent/pauseBtn.png" alt="">
    <img class="stopBtn pointer" @click="stop" src="@/assets/images/Latent/stopBtn.png" alt="">
    <el-progress class="range no-animation-progress" :percentage="progressTwo" :duration="duration" :show-text="false"></el-progress>
    <div class="musicMessage">
      <span class="musicTitle">{{mp3Name}}</span>
      <!-- <span class="musictime">{{ progress }} / {{ duration }}</span> -->
      <span class="musictime">{{ currentTime }} / {{ durationTime }}</span>
    </div>
    <div class="volume" ref="myDiv">
      <img class="volumeBtn pointer" src="@/assets/images/Latent/vlume.png" @click="vlume" alt="">
      <input v-if="userStore.rangeShow" class="volumeShow" type="range" v-model.number="volume" min="0" max="100" @change="setVolume">
    </div>
  </div>
</template>

css样式

<style >
  .musicPlayer{
    display: flex;
    align-items: center;
    position:absolute;
    bottom:0;
    width:100%;
    height: 130px;
    background: rgba(0,0,0,0.45);
    border-radius: 55px;
    .no-animation-progress .el-progress-bar__inner {
      transition: none; /* 这里设置 transition 为 none,禁用动画 */
    }
    .volumeShow{
      position:absolute;
      right:100px;
      bottom:95px;
      width:8px;
      height:130px;
    }
    .playBtn{
      width:40px;
      height:48px;
      margin-left: 95px;
      margin-right: 40px;
    }
    .pauseBtn{
      width:40px;
      height:48px;
      margin-left: 95px;
      margin-right: 40px;
    }
    .stopBtn{
      width:43px;
      height:43px;
      margin-right: 40px;
    }
    .range{
      width:1500px;
      position:relative;
      top:8px;
      .el-progress-bar__outer{
        height: 12px !important;
        border-radius: 5px !important;
        background: #313944 !important;
      }
    }
    input[type=range] {
      -webkit-appearance: slider-vertical;
    }
    .musicMessage{
      position: absolute;
      top:28px;
      width:1500px;
      left:50%;
      margin-left:-700px;
      display: flex;
      justify-content: space-between;
      font-weight: 500;
      font-size: 25px;
      color: #FFFFFF;
      line-height: 35px;
      .musictime{
        margin-right: 5px;
      }
    }
    .volume{
      display:inline-block;
    }
    .volumeBtn{
      width:39px;
      height:30px;
      margin-left: 40px;
    }
  }
</style>