封装后音频播放器样式

大概思路是引入audio音频播放器标签,如果不设置control属性,音频标签就会隐藏,这样我们可以自己写音频播放器的样式,然后调用audio标签的方法,达到封装音频播放器的效果。

需要注意的是audio标签的canplay回调方法是音频加载完成之后调用,这时我们可以在这个回调方法里面获取音频的总长度。

代码如下

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

  <audio ref="audioRef" :src="url" @canplay="onCanplay"></audio>
</template>

<script setup lang="ts">
import { ref, onBeforeMount } from "vue";

withDefaults(defineProps<{
  url: string;
}>(),
  {
    url: "https://xxx.xxx.xx.xx/audio.mp3"
  }
)

const speedList = [
  {
    label: "2x",
    value: 2,
  },
  {
    label: "1.5x",
    value: 1.5,
  },
  {
    label: "1x",
    value: 1,
  },
  {
    label: "0.75x",
    value: 0.75,
  },
];

onBeforeMount(() => {
  clearInterval(timeInterval.value);
});

const speedVisible = ref<boolean>(false); // 设置音频播放速度弹窗
const audioRef = ref(); // 音频标签对象
const activeSpeed = ref(1); // 音频播放速度
const audioDuration = ref(0); // 音频总时长
const audioCurrent = ref(0); // 音频当前播放时间
const audioVolume = ref(1); // 音频声音,范围 0-1
const playStatus = ref<boolean>(false); // 音频播放状态:true 播放,false 暂停
const playProgress = ref(0); // 音频播放进度
const timeInterval = ref(); // 获取音频播放进度定时器

// 音频加载完毕的回调
const onCanplay = () => {
  audioDuration.value = audioRef?.value.duration || 0;
}
const onPlay = async () => {
  // 音频播放完后,重新播放
  if (playProgress.value === 100) audioRef.value.currentTime = 0;

  await audioRef.value.play();
  playStatus.value = true;
  audioDuration.value = audioRef.value.duration;

  timeInterval.value = setInterval(() => {
    audioCurrent.value = audioRef.value.currentTime;
    playProgress.value = (audioCurrent.value / audioDuration.value) * 100;
    if (playProgress.value === 100) onPause();
  }, 100);
};
const onPause = () => {
  audioRef.value.pause();
  playStatus.value = false;
  clearInterval(timeInterval.value);
};
const onChangeSpeed = (value: number) => {
  activeSpeed.value = value;
  // 设置倍速
  audioRef.value.playbackRate = value;
  speedVisible.value = false;
};
const onHandleSpeed = () => {
  speedVisible.value = !speedVisible.value;
};
// 设置声音
const onSetVolume = (value: number) => {
  audioRef.value.volume = value;
  audioVolume.value = value;
};
// 音频播放时间换算
const transTime = (value: number) => {
  let time = "";
  let h = parseInt(String(value / 3600));
  value %= 3600;
  let m = parseInt(String(value / 60));
  let s = parseInt(String(value % 60));
  if (h > 0) {
    time = formatTime(h + ":" + m + ":" + s);
  } else {
    time = formatTime(m + ":" + s);
  }
  return time;
};
// 格式化时间显示,补零对齐
const formatTime = (value: string) => {
  let time = "";
  let s = value.split(":");
  let i = 0;
  for (; i < s.length - 1; i++) {
    time += s[i].length == 1 ? "0" + s[i] : s[i];
    time += ":";
  }
  time += s[i].length == 1 ? "0" + s[i] : s[i];

  return time;
};
</script>

<style lang="less" 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>

Logo

前往低代码交流专区

更多推荐