<template>
<div class="my-audio">
<!-- 音频播放器,使用timeupdate事件更新播放进度 -->
<audio @timeupdate="updateProgress" controls ref="audioRef">
<source :src="audioUrl" type="audio/mpeg" />
您的浏览器不支持音频播放
</audio>
<!-- 音频控制区域 -->
<div class="audio-container">
<!-- 快退、播放/暂停、快进按钮 -->
<div class="audio-controls">
<el-button :disabled="!disabled" circle size="small" link @click="beforeTen">
<svg-icon :size="20" name="fast-recording-before" />
</el-button>
<!-- 播放/暂停按钮,根据audioIsPlay动态改变图标 -->
<el-button :disabled="!disabled" link @click="playAudio" circle class="play-button">
<svg-icon :size="35" :name="audioIsPlay ? 'fast-recording-videopause' : 'fast-recording-circle'" />
</el-button>
<el-button :disabled="!disabled" circle size="small" link @click="afterTen" style="margin-left: 0">
<svg-icon :size="20" name="fast-recording-after" />
</el-button>
</div>
<!-- 播放进度条 -->
<div class="slider-box">
<span>{{ audioStart }}</span>
<el-slider class="slider" v-model="currentProgress" :show-tooltip="false" @input="handleProgressChange" />
<span>{{ durationTime }}</span>
</div>
<!-- 音量控制 -->
<div class="volume" @mouseenter="audioHubs = true" @mouseleave="audioHubs = false">
<div class="volume-progress" v-show="audioHubs">
<el-slider
vertical
height="100px"
class="volume-bar"
v-model="audioVolume"
:show-tooltip="false"
@change="handleAudioVolume" />
</div>
<svg-icon :size="30" class="volume-icon" name="fast-recording-sound"></svg-icon>
</div>
<!-- 下载按钮 -->
<el-popover placement="top" :width="120" trigger="hover">
<template #reference>
<svg-icon name="fast-recording-more"></svg-icon>
</template>
<div style="text-align: center">
<el-button link @click="downloadFullAudio">下载录音</el-button>
</div>
</el-popover>
</div>
</div>
</template>
<script setup>
const props = defineProps({
audioUrl: String, // 音频的URL
flashName: String, // 用于下载音频时的文件名
})
// 是否正在播放
const audioIsPlay = ref(false)
// 音频开始时间显示
const audioStart = ref('0:00')
// 音频总时长显示
const durationTime = ref('0:00')
// 音频总时长
const duration = ref(0)
// 音量控制
const audioVolume = ref(80)
// 是否显示音量控制滑块
const audioHubs = ref(false)
// 音频元素引用
const audioRef = ref(null)
// 当前播放进度
const currentProgress = ref(0)
// 是否禁用控制按钮
const disabled = ref(false)
// 监听音频总时长变化,禁用快进和快退按钮
watch(
() => duration.value,
(newVal) => {
if (newVal) {
disabled.value = true
}
}
)
// 监听音频URL变化,重新计算音频时长
watch(
() => props.audioUrl,
(newVal) => {
calculateDuration()
}
)
// 异步获取音频时长
const getAudioDuration = async (blobUrl) => {
const audioContext = new (window.AudioContext || window.webkitAudioContext)()
try {
const response = await fetch(blobUrl)
const blob = await response.blob()
const arrayBuffer = await new Response(blob).arrayBuffer()
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer)
return Number(audioBuffer.duration.toFixed(0))
} catch (error) {
console.error('获取音频时长时发生错误:', error)
}
}
// 计算并设置音频的总时长
const calculateDuration = async () => {
let myVid = audioRef.value
myVid.loop = false
// 监听音频播放结束事件
myVid.addEventListener('ended', function () {
audioIsPlay.value = false
currentProgress.value = 0
audioStart.value = transTime(0)
})
if (myVid !== null) {
myVid.src = props.audioUrl
duration.value = await getAudioDuration(props.audioUrl)
durationTime.value = transTime(duration.value)
myVid.volume = 0.8 // 设置默认音量为80%
}
}
// 将秒转换为分钟:秒的格式
const transTime = (duration) => {
const minutes = Math.floor(duration / 60)
const seconds = Math.floor(duration % 60)
const formattedMinutes = String(minutes).padStart(2, '0')
const formattedSeconds = String(seconds).padStart(2, '0')
return `${formattedMinutes}:${formattedSeconds}`
}
// 播放或暂停音频
const playAudio = () => {
if (!audioIsPlay.value) {
audioRef.value.play()
audioIsPlay.value = true
} else {
audioRef.value.pause()
audioIsPlay.value = false
}
}
// 更新播放进度
const updateProgress = (e) => {
let value = e.target.currentTime / duration.value
if (audioRef.value.play) {
currentProgress.value = value * 100
audioStart.value = transTime(audioRef.value.currentTime)
}
}
// 调整播放进度
const handleProgressChange = (val) => {
if (!val) {
return
}
audioRef.value.currentTime = duration.value * (val / 100)
}
// 调整音量
const handleAudioVolume = (val) => {
audioRef.value.volume = val / 100
}
// 快退10秒
const beforeTen = () => {
audioRef.value.currentTime -= 10
}
// 快进10秒
const afterTen = () => {
audioRef.value.currentTime += 10
}
// 下载音频
const downloadFullAudio = () => {
const link = document.createElement('a')
link.href = props.audioUrl
link.download = `${props.flashName}.wav`
link.click()
}
</script>
<style lang="scss" scoped>
.my-audio {
audio {
display: none;
}
.audio-container {
width: 100%;
height: 50px;
display: flex;
align-items: center;
border-radius: 4px;
box-sizing: border-box;
position: relative;
.audio-controls {
display: flex;
margin-right: 16px;
.play-button {
margin: 0 8px;
}
}
.slider-box {
flex: 1;
display: flex;
align-items: center;
span {
font-size: 12px;
color: #49505c;
line-height: 18px;
}
.slider {
margin: 0 20px;
}
:deep(.elp-slider__bar) {
background: transparent linear-gradient(to right, #8d00ff, #005fff);
}
:deep(.elp-slider__button) {
border: 3px solid #8d00ff;
}
}
}
.volume {
position: relative;
width: 30px;
height: 30px;
margin: 0 24px;
display: flex;
align-items: center;
.volume-progress {
width: 32px;
height: 140px;
position: absolute;
top: -142px;
right: 10%;
}
.volume-bar {
background: #ffffff;
box-shadow: 0 2px 14px 0 rgba(0, 0, 0, 0.12);
border-radius: 8px;
:deep(.elp-slider__bar) {
background-color: #13d6e5;
}
}
.volume-icon {
padding: 5px;
cursor: pointer;
}
}
}
</style>
其中svg-icon是icon组件,可以直接使用element组件中的icon图标