vue3+ts 音频播放功能(多个音频播放器)

<template>
  <div v-for="(item, index) in list" :key="index">
    <div class="audio-player">
      <img
        src="../.../assets/images/home/shopping"
        alt=""
        class="play-icon"
        @click="onTogglePlay(index)"
        v-if="!isPlaying(index)"
      />
      <img
        src="../.../assets/images/home/shopping"
        alt=""
        class="play-icon"
        @click="onTogglePlay(index)"
        v-else
      />
      <span class="play-time">
        {{ transTime(audioCurrent[index]) }}/{{ transTime(audioDuration[index]) }}
      </span>
      <div class="play-progress">
        <div
          class="play-current-progress"
          :style="{ width: `${playProgress[index]}%` }"
        ></div>
      </div>
      <img
        src="../.../assets/images/home/shopping"
        alt=""
        class="play-voice"
        v-if="audioVolume[index] === 1"
        @click="onSetVolume(index, 0)"
      />
      <img
        src="../.../assets/images/home/shopping"
        alt=""
        class="play-voice"
        v-else
        @click="onSetVolume(index, 1)"
      />
      <el-popover v-model:visible="speedVisible[index]" placement="top" :width="50">
        <div
          v-for="speedItem in speedList"
          :key="speedItem.value"
          @click="onChangeSpeed(index, speedItem.value)"
          style="margin-bottom: 17px; cursor: pointer; text-align: center"
        >
          <span>{{ speedItem.label }}</span>
        </div>
        <template #reference>
          <span 
            class="play-speed"
            @click="onHandleSpeed(index)"
          >{{ activeSpeed[index] }}x</span>
        </template>
      </el-popover>
    </div>

    <audio :ref="setAudioRef(index)" :src="item.url" @canplay="onCanplay(index)" @timeupdate="onTimeUpdate(index)"></audio>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';

const list = ref([
  { url: 'https://jbkj.obs.cn-east-3.myhuaweicloud.com/data/audioFile/%E5%91%A8%E6%9D%B0%E4%BC%A6-%E4%B8%8A%E6%B5%B7%E4%B8%80%E4%B9%9D%E5%9B%9B%E4%B8%89.mp3' },
  { url: 'https://jbkj.obs.cn-east-3.myhuaweicloud.com/data/audioFile/%E5%91%A8%E6%9D%B0%E4%BC%A6-%E4%B8%8A%E6%B5%B7%E4%B8%80%E4%B9%9D%E5%9B%9B%E4%B8%89.mp3' },
  // 你可以在这里添加更多的音频URL
]);

const audioRefs = ref([]);
const playStatus = ref([]);
const audioCurrent = ref([]);
const audioDuration = ref([]);
const playProgress = ref([]);
const audioVolume = ref([]);
const speedVisible = ref([]);
const activeSpeed = ref([]);
const speedList = ref([
  { value: 0.5, label: '0.5x' },
  { value: 1, label: '1x' },
  { value: 1.5, label: '1.5x' },
  { value: 2, label: '2x' }
]);

// 初始化状态
onMounted(() => {
  list.value.forEach((_, index) => {
    playStatus.value[index] = false;
    audioCurrent.value[index] = 0;
    audioDuration.value[index] = 0;
    playProgress.value[index] = 0;
    audioVolume.value[index] = 1;
    speedVisible.value[index] = false;
    activeSpeed.value[index] = 1;
  });
});

// 设置音频引用
const setAudioRef = (index) => (el) => {
  audioRefs.value[index] = el;
};

const onTogglePlay = (index) => {
  const audio = audioRefs.value[index];
  if (audio) {
    if (isPlaying(index)) {
      audio.pause();
      playStatus.value[index] = false;
    } else {
      onStopAllAudio();
      audio.play();
      playStatus.value[index] = true;
    }
  }
};

const onStopAllAudio = () => {
  audioRefs.value.forEach((audio, index) => {
    if (audio && isPlaying(index)) {
      audio.pause();
      playStatus.value[index] = false;
    }
  });
};

const isPlaying = (index) => {
  return playStatus.value[index] === true;
};

const onCanplay = (index) => {
  const audio = audioRefs.value[index];
  if (audio) {
    audioDuration.value[index] = audio.duration;
  }
};

const onTimeUpdate = (index) => {
  const audio = audioRefs.value[index];
  if (audio) {
    audioCurrent.value[index] = audio.currentTime;
    playProgress.value[index] = (audioCurrent.value[index] / audioDuration.value[index]) * 100;
  }
};

const onSetVolume = (index, volume) => {
  const audio = audioRefs.value[index];
  if (audio) {
    audio.volume = volume;
    audioVolume.value[index] = volume;
  }
};

const onChangeSpeed = (index, speed) => {
  const audio = audioRefs.value[index];
  if (audio) {
    audio.playbackRate = speed;
    activeSpeed.value[index] = speed;
    speedVisible.value[index] = false;
  }
};

const onHandleSpeed = (index) => {
  speedVisible.value[index] = !speedVisible.value[index];
};

const transTime = (time) => {
  // 转换时间格式
  const minutes = Math.floor(time / 60);
  const seconds = Math.floor(time % 60);
  return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;
};
</script>
 
<style lang="scss" scoped>
.audio-player {
  width: 378px;
  height: 52px;
  background: linear-gradient(180deg, #505572 0%, #383b4f 100%);
  border-radius: 8px;
  padding: 9px 11px;
  margin: 40px 26px 0;
  box-sizing: border-box;
  display: flex;
  align-items: center;
 
  .play-icon {
    width: 34px;
    height: 34px;
    margin-right: 7px;
    cursor: pointer;
  }
 
  .play-time {
    width: 72px;
    display: inline-block;
    margin-right: 16px;
  }
 
  .play-progress {
    width: 160px;
    height: 4px;
    background-color: #323547;
    box-shadow: inset 0px 1px 0px 0px #20222d;
    border-radius: 2px;
    margin-right: 16px;
    position: relative;
    .play-current-progress {
      height: 4px;
      background: #00e5ff;
      border-radius: 2px;
      position: absolute;
      top: 0;
      left: 0;
    }
  }
 
  .play-voice {
    width: 20px;
    height: 20px;
    margin-right: 14px;
    cursor: pointer;
  }
 
  .play-speed {
    cursor: pointer;
    color: #00e5ff;
  }
}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值