【组件开发】基于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图标

posted @ 2024-07-24 17:19  狂扇赵四那半拉好嘴  阅读(7)  评论(0编辑  收藏  举报