【组件开发】基于elementplus组件开发的audio音频播放器
1 <template> 2 <div class="my-audio"> 3 <!-- 音频播放器,使用timeupdate事件更新播放进度 --> 4 <audio @timeupdate="updateProgress" controls ref="audioRef"> 5 <source :src="audioUrl" type="audio/mpeg" /> 6 您的浏览器不支持音频播放 7 </audio> 8 <!-- 音频控制区域 --> 9 <div class="audio-container"> 10 <!-- 快退、播放/暂停、快进按钮 --> 11 <div class="audio-controls"> 12 <el-button :disabled="!disabled" circle size="small" link @click="beforeTen"> 13 <svg-icon :size="20" name="fast-recording-before" /> 14 </el-button> 15 <!-- 播放/暂停按钮,根据audioIsPlay动态改变图标 --> 16 <el-button :disabled="!disabled" link @click="playAudio" circle class="play-button"> 17 <svg-icon :size="35" :name="audioIsPlay ? 'fast-recording-videopause' : 'fast-recording-circle'" /> 18 </el-button> 19 <el-button :disabled="!disabled" circle size="small" link @click="afterTen" style="margin-left: 0"> 20 <svg-icon :size="20" name="fast-recording-after" /> 21 </el-button> 22 </div> 23 <!-- 播放进度条 --> 24 <div class="slider-box"> 25 <span>{{ audioStart }}</span> 26 <el-slider class="slider" v-model="currentProgress" :show-tooltip="false" @input="handleProgressChange" /> 27 <span>{{ durationTime }}</span> 28 </div> 29 <!-- 音量控制 --> 30 <div class="volume" @mouseenter="audioHubs = true" @mouseleave="audioHubs = false"> 31 <div class="volume-progress" v-show="audioHubs"> 32 <el-slider 33 vertical 34 height="100px" 35 class="volume-bar" 36 v-model="audioVolume" 37 :show-tooltip="false" 38 @change="handleAudioVolume" /> 39 </div> 40 <svg-icon :size="30" class="volume-icon" name="fast-recording-sound"></svg-icon> 41 </div> 42 <!-- 下载按钮 --> 43 <el-popover placement="top" :width="120" trigger="hover"> 44 <template #reference> 45 <svg-icon name="fast-recording-more"></svg-icon> 46 </template> 47 <div style="text-align: center"> 48 <el-button link @click="downloadFullAudio">下载录音</el-button> 49 </div> 50 </el-popover> 51 </div> 52 </div> 53 </template> 54 55 <script setup> 56 const props = defineProps({ 57 audioUrl: String, // 音频的URL 58 flashName: String, // 用于下载音频时的文件名 59 }) 60 61 // 是否正在播放 62 const audioIsPlay = ref(false) 63 // 音频开始时间显示 64 const audioStart = ref('0:00') 65 // 音频总时长显示 66 const durationTime = ref('0:00') 67 // 音频总时长 68 const duration = ref(0) 69 // 音量控制 70 const audioVolume = ref(80) 71 // 是否显示音量控制滑块 72 const audioHubs = ref(false) 73 // 音频元素引用 74 const audioRef = ref(null) 75 // 当前播放进度 76 const currentProgress = ref(0) 77 78 // 是否禁用控制按钮 79 const disabled = ref(false) 80 // 监听音频总时长变化,禁用快进和快退按钮 81 watch( 82 () => duration.value, 83 (newVal) => { 84 if (newVal) { 85 disabled.value = true 86 } 87 } 88 ) 89 // 监听音频URL变化,重新计算音频时长 90 watch( 91 () => props.audioUrl, 92 (newVal) => { 93 calculateDuration() 94 } 95 ) 96 97 // 异步获取音频时长 98 const getAudioDuration = async (blobUrl) => { 99 const audioContext = new (window.AudioContext || window.webkitAudioContext)() 100 try { 101 const response = await fetch(blobUrl) 102 const blob = await response.blob() 103 const arrayBuffer = await new Response(blob).arrayBuffer() 104 const audioBuffer = await audioContext.decodeAudioData(arrayBuffer) 105 return Number(audioBuffer.duration.toFixed(0)) 106 } catch (error) { 107 console.error('获取音频时长时发生错误:', error) 108 } 109 } 110 111 // 计算并设置音频的总时长 112 const calculateDuration = async () => { 113 let myVid = audioRef.value 114 myVid.loop = false 115 116 // 监听音频播放结束事件 117 myVid.addEventListener('ended', function () { 118 audioIsPlay.value = false 119 currentProgress.value = 0 120 audioStart.value = transTime(0) 121 }) 122 123 if (myVid !== null) { 124 myVid.src = props.audioUrl 125 duration.value = await getAudioDuration(props.audioUrl) 126 durationTime.value = transTime(duration.value) 127 myVid.volume = 0.8 // 设置默认音量为80% 128 } 129 } 130 131 // 将秒转换为分钟:秒的格式 132 const transTime = (duration) => { 133 const minutes = Math.floor(duration / 60) 134 const seconds = Math.floor(duration % 60) 135 const formattedMinutes = String(minutes).padStart(2, '0') 136 const formattedSeconds = String(seconds).padStart(2, '0') 137 return `${formattedMinutes}:${formattedSeconds}` 138 } 139 140 // 播放或暂停音频 141 const playAudio = () => { 142 if (!audioIsPlay.value) { 143 audioRef.value.play() 144 audioIsPlay.value = true 145 } else { 146 audioRef.value.pause() 147 audioIsPlay.value = false 148 } 149 } 150 151 // 更新播放进度 152 const updateProgress = (e) => { 153 let value = e.target.currentTime / duration.value 154 if (audioRef.value.play) { 155 currentProgress.value = value * 100 156 audioStart.value = transTime(audioRef.value.currentTime) 157 } 158 } 159 160 // 调整播放进度 161 const handleProgressChange = (val) => { 162 if (!val) { 163 return 164 } 165 audioRef.value.currentTime = duration.value * (val / 100) 166 } 167 168 // 调整音量 169 const handleAudioVolume = (val) => { 170 audioRef.value.volume = val / 100 171 } 172 173 // 快退10秒 174 const beforeTen = () => { 175 audioRef.value.currentTime -= 10 176 } 177 178 // 快进10秒 179 const afterTen = () => { 180 audioRef.value.currentTime += 10 181 } 182 183 // 下载音频 184 const downloadFullAudio = () => { 185 const link = document.createElement('a') 186 link.href = props.audioUrl 187 link.download = `${props.flashName}.wav` 188 link.click() 189 } 190 </script> 191 192 <style lang="scss" scoped> 193 .my-audio { 194 audio { 195 display: none; 196 } 197 .audio-container { 198 width: 100%; 199 height: 50px; 200 display: flex; 201 align-items: center; 202 border-radius: 4px; 203 box-sizing: border-box; 204 position: relative; 205 .audio-controls { 206 display: flex; 207 margin-right: 16px; 208 .play-button { 209 margin: 0 8px; 210 } 211 } 212 .slider-box { 213 flex: 1; 214 display: flex; 215 align-items: center; 216 span { 217 font-size: 12px; 218 color: #49505c; 219 line-height: 18px; 220 } 221 .slider { 222 margin: 0 20px; 223 } 224 :deep(.elp-slider__bar) { 225 background: transparent linear-gradient(to right, #8d00ff, #005fff); 226 } 227 :deep(.elp-slider__button) { 228 border: 3px solid #8d00ff; 229 } 230 } 231 } 232 .volume { 233 position: relative; 234 width: 30px; 235 height: 30px; 236 margin: 0 24px; 237 display: flex; 238 align-items: center; 239 .volume-progress { 240 width: 32px; 241 height: 140px; 242 position: absolute; 243 top: -142px; 244 right: 10%; 245 } 246 .volume-bar { 247 background: #ffffff; 248 box-shadow: 0 2px 14px 0 rgba(0, 0, 0, 0.12); 249 border-radius: 8px; 250 :deep(.elp-slider__bar) { 251 background-color: #13d6e5; 252 } 253 } 254 .volume-icon { 255 padding: 5px; 256 cursor: pointer; 257 } 258 } 259 } 260 </style>
其中svg-icon是icon组件,可以直接使用element组件中的icon图标