首页 > 其他分享 >【组件开发】基于elementplus组件开发的audio音频播放器

【组件开发】基于elementplus组件开发的audio音频播放器

时间:2024-07-24 17:31:53浏览次数:10  
标签:elementplus const 音频 value duration 组件 audioRef audio ref

  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图标

标签:elementplus,const,音频,value,duration,组件,audioRef,audio,ref
From: https://www.cnblogs.com/leileig/p/18321324

相关文章

  • 04、组件介绍
    k8s里的资源对象在k8s里,yaml用来声明API对象的,那么API对象都有哪些?可以这样查看资源对象kubectlapi-resourcesyaml使用缩进表示层次,缩进不允许使用tab,只能用空格,缩进空格数多少不要求,只要保证同一层级空格数一样多即可使用#书写注释数组(列表)是使用-开头的清单形式对......
  • vue.extend中动态加入组件之后组件中的点击事件不能用
    vue.extend中动态加入组件之后组件中的点击事件不能用在Vue.js中,如果你在vue.extend中动态地添加了一个组件,并且发现这个组件的点击事件无法触发,可能的原因和解决方法如下:原因:事件绑定没有正确执行。事件绑定的元素在组件加载时不存在。事件绑定的元素在点击事件被绑定时已......
  • 轮船控制系统nmea2000电缆组件 7/8 T型连接器
    NMEA20007/8T型连接器概述NMEA20007/8T型连接器是专为船舶控制系统设计的电缆组件,主要用于连接船上的各种电子设备和系统,如GPS接收器、自动驾驶仪、风速和风向传感器、深度声纳等。这些设备通过NMEA2000总线共享数据,包括导航信息、位置、深度、速度、风速和风向等关键......
  • NMEA2000在船舶控制系统中航空插头插座组件特性
    NMEA2000在船舶控制系统中的应用概述NMEA2000协议是船舶电子设备之间通信的国际标准,广泛应用于船舶导航、监控和自动化系统。它基于CAN(ControllerAreaNetwork)总线技术,以确保在恶劣环境下的可靠性和效率。NMEA2000协议定义了标准化的物理接口和通信协议,使得不同厂商生产的设......
  • Vue子组件向父组件传递事件
    注:Vue3版本主要流程:1、父组件向子组件传递监听事件点击查看代码<myComponent@getMessage="getMessage"/>2、子组件使用emit机制添加要监听的事件点击查看代码constemit=defineEmits(['getMessage'])3、子组件添加点击事件,点击事件触发时调用监听事件并传递数据......
  • Milvus 核心组件(3)--- MinIO详解
    目录背景MinIO安装dockerdesktop安装UbuntuUI在docker中的安装Minio下载及安装启动miniodockerimage保存启动minioweb网页下一次启动MinIO基本概念基本概述主要特性应用场景MinIO使用连接server创建bucket查询bucket上传文件到bucket一般用......
  • 推荐一款将控制台程序部署到Windows服务的组件
    在日常开发中,有时候需要将我们的程序随着操作系统一起运行,并且无需人工干预。要实现这种效果,有很多种方法,比如:如果是桌面程序,可以设置到程序的启动项;如果是Web程序,还可以托管到IIS中,而对于控制台程序,最常见在做法是将程序部署成Windows服务,并设置成自动运行,这样当操作系统开机时,就......
  • React中函数组件中闭包陷阱如何产生,如何解决?
    在什么情况下会产生闭包陷阱?在React中,当使用useState和useEffect以及useCallback时,我们必须得注意闭包陷阱,避免出现一些意外的行为什么是闭包陷阱?闭包是指一个函数可以访问其词法作用域之外的变量。闭包主要发生的集中情况?在useState中的闭包陷阱在useEffect中的闭......
  • 探索绝对值码盘接口系列板卡:高精度测量与控制的关键组件
    天津拓航科技绝对值码盘卡在当今高度自动化和精密制造的时代,准确获取位置、角度和速度等信息对于许多工业和科技应用至关重要。绝对值码盘接口系列板卡作为实现这一目标的重要工具,正发挥着日益关键的作用。绝对值码盘接口系列板卡,顾名思义,是专门设计用于与绝对值码盘进行连接和......
  • VMware Tools 12.4.5 下载 - 客户机操作系统无缝交互必备组件
    VMwareTools12.4.5下载-客户机操作系统无缝交互必备组件VMware虚拟机必备组件(驱动和交互式服务)请访问原文链接:https://sysin.org/blog/vmware-tools-12/,查看最新版。原创作品,转载请保留出处。作者主页:sysin.orgVMwareTools是一套安装在虚拟机的操作系统中的实用程......