Vue3自定义封装音频播放器

本片主要讲解如何使用Vue3去封装一个音频播放器,以及解决在ios微信浏览器中 无法获取音频时长问题

一、效果图

在这里插入图片描述

二、解决在微信环境中ios无法获取音频时长问题

1、安装weixin-js-sdk

cnpm i weixin-js-sdk --save

2、使用,通过wx.ready中重新去加载音频资源

 wx.config({
  // 可无需配置配置相关参数
  debug: false,
  appId: '',
  timestamp: 1,
  nonceStr: '',
  signature: '',
  jsApiList: []
});
wx.ready(function () {
  audioInfo.audioObject.load()
});  

三、组件封装,整体代码

1、创建AudioCom.vue文件

<template>
  <div class="audio-box">
    <div class="action-box">
      <div class="actions">
        <van-slider
          :max="audioInfo.totalTime"
          bar-height="3"
          button-size="17"
          inactive-color="#F0F0F0"
          active-color="#ffd55a"
          v-model="audioInfo.timeNow"
          @change="onChange"
          :disabled="!audioInfo.status"
          @drag-start="dragState(true)"
          @drag-end="dragState(false)"
        >
          <template #button>
            <div class="custom-button" />
          </template>
        </van-slider>
      </div>
      <div class="time-row">
        <span>{{ getStringTime(audioInfo.timeNow) }}</span>
        <span>{{ getStringTime(audioInfo.totalTime) }}</span>
      </div>
    </div>
    <div class="play-control hover" @click="playOrPause" ontouchstart>
      <div v-if="!audioInfo.loading">
        <van-icon name="play" v-show="!audioInfo.status" />
        <van-icon name="pause" v-show="audioInfo.status" />
      </div>
      <van-loading v-else  color="#FF980F"/>
    </div>
  </div>
</template>

<script lang='ts' setup>
import { audioInit } from './index.hook'
const props = withDefaults(defineProps<{
  src?: string,
}>(), {
  src: ''
})

const { dragState, getStringTime, playOrPause, onChange, audioInfo } = audioInit(props)

</script>
<style lang="scss" scoped>
.audio-box {
  display: flex;
  width: 100%;
  align-items: center;
  margin-left: -10px;
  margin-top: 20px;
  .play-control {
    width: 62px;
    height: 62px;
    border-radius: 50%;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    margin-left: 6px;
    justify-content: center;
    border: 2px solid #FF980F;
    >div {
      display: flex;
      justify-content: center;
      align-items: center;
    }
    i {
      font-size: 45px;
      color: #FF980F;
    }
  }
  .action-box {
    flex: 1;
    position: relative;
    min-width: 0;
    margin-top: 20px;
    .time-row {
      display: flex;
      align-items: center;
      justify-content: space-between;
      font-size: 24px;
      color: #999999;
      margin-top: 20px;
      box-sizing: border-box;
      padding: 0 15px;
    }
    .actions {
      display: flex;
      align-items: center;
      >span {
        font-size: 22px;
        color: #838383;
      }
      :deep(.van-slider) {
        flex: 1;
        margin: 0 20px;
      }
    }
  }
}
.custom-button {
    width: 10px;
    height: 26px;
    color: #fff;
    font-size: 10px;
    line-height: 18px;
    text-align: center;
    background-color: #FF980F;
    border-radius: 100px;
}
</style>

2、创建index.hook.ts文件

import { reactive, watch } from 'vue'
import wx from 'weixin-js-sdk'

export const audioInit = (props: { src: string }) => {
  wx.config({
    // 配置信息, 即使不正确也能使用 wx.ready
    debug: false,
    appId: '',
    timestamp: 1,
    nonceStr: '',
    signature: '',
    jsApiList: []
  });
  const audioInfo: any = reactive({
    audioObject: null,
    timeNow: 0,
    totalTime: 1.5,
    status: false,
    isSlide: false,
    loading: false
  })
  const dragState = (bool: boolean) => {
    audioInfo.isSlide = bool;
  }
  const onChange = (value: number) => {
    audioInfo.audioObject.currentTime = value;
  }
  const playOrPause = () => {
    if (audioInfo.totalTime === 1.5 || audioInfo.totalTime === '00:00') {
      audioInfo.loading = true
      return
    }

    if (audioInfo.status) {
      audioInfo.audioObject.pause();
      audioInfo.status = false;
    } else {
      audioInfo.audioObject.play();
      audioInfo.status = true;
    }
  }
  const getStringTime = (time: any) => {
    if (time === 1.5 || isNaN(time)) {
      return "00:00"
    }
    let minute: number | string = parseInt(time / 60);
    if (minute < 10) {
      minute = `0${minute}`;
    }
    let second: number | string = parseInt(time % 60);
    if (second < 10) {
      second = `0${second}`;
    }
    return minute + ":" + second;
  }

  const resetState = () => {
    audioInfo.audioObject = null
    audioInfo.totalTime = 1.5
    audioInfo.timeNow = 0
    audioInfo.status = false
    audioInfo.isSlide = false
    audioInfo.loading = false
  }
  
  // 判断是否是ios环境
  const  is_iOS = () => {
    return /(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent);
  }

  const createAudio = () => {
    audioInfo.audioObject = new Audio(props.src);
    audioInfo.audioObject.addEventListener(is_iOS() ? "loadedmetadata" : "loadeddata", function () {
      audioInfo.totalTime = parseInt(audioInfo.audioObject.duration);
      audioInfo.loading = false
    });
    audioInfo.audioObject.addEventListener("timeupdate", function () {
      let tempSecond = parseInt(audioInfo.audioObject.currentTime);
      if (audioInfo.timeNow != tempSecond && !audioInfo.isSlide) {
        audioInfo.timeNow = tempSecond;
      }
    });
    audioInfo.audioObject.addEventListener("ended", function () {
      audioInfo.status = false;
      audioInfo.timeNow = 0;
    });
    wx.ready(function () {
      audioInfo.audioObject.load()
    });
  }

  watch(() => props.src, (news) => {
    resetState()
    if (news) {
      createAudio()
    }
  }, {
    deep: true,
    immediate: true
  })
  return {
    dragState,
    getStringTime,
    playOrPause,
    onChange,
    audioInfo
  }
}

大功告成

你可能感兴趣的:(音视频,vue3音频组件,vue封装音频组件,audio时长问题)