手写一个audio播放器,实现歌曲切换,列表歌曲循环,音量调节等 vue组件
1 <template>
2 <div class="wrapper">
3 <svg
4 t="1673833915638"
5 class="icon rotateIcon"
6 viewBox="0 0 1024 1024"
7 version="1.1"
8 xmlns="http://www.w3.org/2000/svg"
9 p-id="2105"
10 >
11 <path
12 d="M478.388706 45.236706L363.52 2.529882v668.431059a254.674824 254.674824 0 0 0-115.892706 18.010353c-96.015059 39.213176-145.889882 133.541647-118.362353 199.499294 27.587765 66.138353 122.217412 96.617412 218.112 57.404236 73.728-30.117647 124.867765-87.401412 131.553883-142.275765l-0.481883-591.329883c158.238118 48.067765 272.263529 100.773647 272.26353 258.048 0 55.838118 91.557647 109.808941 91.557647-109.146352-0.060235-186.247529-159.804235-298.887529-363.881412-315.934118z"
13 fill="#17966f"
14 p-id="2106"
15 />
16 </svg>
17 <img
18 class="musicIcon"
19 :src="musicList[currentIndex].icon"
20 :style="{ animationPlayState: `${playStatus ? 'running' : 'paused'}` }"
21 />
22 <div class="controls">
23 <div class="controls-title">
24 正在播放中:{{ musicList[currentIndex].singer }} -
25 {{ musicList[currentIndex].song }}
26 </div>
27 <audio
28 ref="audio"
29 :src="musicList[currentIndex].url"
30 @ended="ended"
31 autoplay
32 />
33 <div class="controls-change">
34 <svg
35 t="1673834949112"
36 class="icon"
37 viewBox="0 0 1024 1024"
38 version="1.1"
39 xmlns="http://www.w3.org/2000/svg"
40 p-id="1372"
41 @click="preMusic"
42 >
43 <path
44 d="M76.442058 472.123804l479.972785-344.375282c31.816917-17.080004 68.268577-21.123087 68.295184 44.618197l0 149.899008 271.113573-194.517205c31.815893-17.080004 69.179357-17.590634 68.295184 46.384423l0 676.471912c-0.91078 57.779961-39.127716 66.168002-68.295184 47.295166L624.711051 703.367469l0 148.120502c-0.026607 56.012711-39.127716 65.284889-68.295184 46.411029L76.442058 553.481763C54.339786 531.021216 54.339786 494.597655 76.442058 472.123804zM907.552193 196.359156 568.143437 433.700703c0 0 0-186.833199 0-237.341546L115.598428 512.809435l452.545009 316.465628c0-13.244652 0-237.328243 0-237.328243l339.408757 237.328243C907.552193 816.029388 907.552193 246.867504 907.552193 196.359156z"
45 fill="#0fd7ce"
46 p-id="1373"
47 />
48 </svg>
49 <svg
50 t="1673835088221"
51 class="icon"
52 viewBox="0 0 1024 1024"
53 version="1.1"
54 xmlns="http://www.w3.org/2000/svg"
55 p-id="1230"
56 @click="play"
57 v-if="!playStatus"
58 >
59 <path
60 d="M512 0C230.4 0 0 230.4 0 512s230.4 512 512 512 512-230.4 512-512S793.6 0 512 0z m0 981.333333C253.866667 981.333333 42.666667 770.133333 42.666667 512S253.866667 42.666667 512 42.666667s469.333333 211.2 469.333333 469.333333-211.2 469.333333-469.333333 469.333333z"
61 fill="#0fd7ce"
62 p-id="1231"
63 />
64 <path
65 d="M672 441.6l-170.666667-113.066667c-57.6-38.4-106.666667-12.8-106.666666 57.6v256c0 70.4 46.933333 96 106.666666 57.6l170.666667-113.066666c57.6-42.666667 57.6-106.666667 0-145.066667z"
66 fill="#0fd7ce"
67 p-id="1232"
68 />
69 </svg>
70 <svg
71 t="1673835567808"
72 class="icon"
73 viewBox="0 0 1048 1024"
74 version="1.1"
75 xmlns="http://www.w3.org/2000/svg"
76 p-id="1531"
77 @click="pause"
78 v-else
79 >
80 <path
81 d="M524.272128 0.018022C241.513165 0.018022 12.288102 229.245952 12.288102 512.005018c0 112.734003 36.43904 216.957952 98.17897 301.537997l38.667981-40.258048C97.458176 699.230003 67.143168 609.158963 67.143168 512.005018 67.143168 259.540992 271.807078 54.872986 524.272128 54.872986c252.45696 0 457.120973 204.668006 457.120973 457.132032 0 252.460954-204.664013 457.118003-457.120973 457.118003-96.240026 0-185.530982-29.744026-259.189043-80.53504l-34.539008 42.797978c83.150029 58.344038 184.437965 92.596019 293.728973 92.596019 282.758963 0 511.984026-229.220966 511.984026-511.976038C1036.256154 229.245952 807.031091 0.018022 524.272128 0.018022zM615.693107 256.011981l0 511.987 54.855 0L670.548107 256.012 615.693128 256.012zM377.996083 256.011981l0 511.987 54.855 0L432.851083 256.012 377.996128 256.012z"
82 fill="#0fd7ce"
83 p-id="1532"
84 />
85 </svg>
86 <svg
87 t="1673835103937"
88 class="icon"
89 viewBox="0 0 1024 1024"
90 version="1.1"
91 xmlns="http://www.w3.org/2000/svg"
92 p-id="1381"
93 @click="nextMusic"
94 >
95 <path
96 d="M947.557942 553.481763 467.585156 897.899c-29.166445 18.872836-68.267554 9.601682-68.295184-46.411029L399.289972 703.367469 128.175376 897.899c-29.166445 18.872836-67.384404 10.484795-68.295184-47.295166L59.880192 174.132946c-0.883149-63.975057 36.479291-63.464427 68.295184-46.384423l271.113573 194.517205L399.288949 172.366719c0.02763-65.741283 36.479291-61.698201 68.295184-44.618197l479.973809 344.375282C969.661238 494.597655 969.661238 531.021216 947.557942 553.481763zM116.44883 829.275064l339.408757-237.328243c0 0 0 224.082568 0 237.328243l452.545009-316.465628-452.545009-316.450279c0 50.508347 0 237.341546 0 237.341546L116.44883 196.359156C116.44883 246.867504 116.44883 816.029388 116.44883 829.275064z"
97 fill="#0fd7ce"
98 p-id="1382"
99 />
100 </svg>
101 </div>
102 <div class="controls-progress">
103 <span class="controls-progress-duration"
104 >{{ Math.floor(currentTime / 60) }}:{{
105 Math.floor(currentTime % 60) < 10
106 ? `0${Math.floor(currentTime % 60)}`
107 : Math.floor(currentTime % 60)
108 }}
109 / {{ Math.floor(totalDuration / 60) }}:{{
110 Math.floor(totalDuration % 60) < 10
111 ? `0${Math.floor(totalDuration % 60)}`
112 : Math.floor(totalDuration % 60)
113 }}
114 </span>
115 <input
116 class="controls-progress-range"
117 type="range"
118 :max="totalDuration"
119 :value="currentTime"
120 :style="{
121 backgroundSize: `${(currentTime / totalDuration) * 100}% 100%`
122 }"
123 @change="changePlayProgress"
124 />
125 <svg
126 t="1673839083338"
127 class="icon volume-icon"
128 viewBox="0 0 1024 1024"
129 version="1.1"
130 xmlns="http://www.w3.org/2000/svg"
131 p-id="1290"
132 @click="volumeStatus = !volumeStatus"
133 v-if="!muteStatus"
134 >
135 <path
136 d="M448 938.666667a21.333333 21.333333 0 0 1-15.093333-6.246667L225.833333 725.333333H53.333333a53.393333 53.393333 0 0 1-53.333333-53.333333V352a53.393333 53.393333 0 0 1 53.333333-53.333333h172.5l207.08-207.086667A21.333333 21.333333 0 0 1 469.333333 106.666667v810.666666a21.333333 21.333333 0 0 1-21.333333 21.333334zM53.333333 341.333333a10.666667 10.666667 0 0 0-10.666666 10.666667v320a10.666667 10.666667 0 0 0 10.666666 10.666667h181.333334a21.333333 21.333333 0 0 1 15.086666 6.246666L426.666667 865.833333V158.166667L249.753333 335.086667A21.333333 21.333333 0 0 1 234.666667 341.333333z"
137 fill="#0fd7ce"
138 p-id="1291"
139 />
140 </svg>
141 <svg
142 t="1673845914200"
143 class="icon volume-icon"
144 viewBox="0 0 1024 1024"
145 version="1.1"
146 xmlns="http://www.w3.org/2000/svg"
147 p-id="1440"
148 @click="volumeStatus = !volumeStatus"
149 v-else
150 >
151 <path
152 d="M448 938.666667a21.333333 21.333333 0 0 1-15.093333-6.246667L225.833333 725.333333H53.333333a53.393333 53.393333 0 0 1-53.333333-53.333333V352a53.393333 53.393333 0 0 1 53.333333-53.333333h172.5l207.08-207.086667A21.333333 21.333333 0 0 1 469.333333 106.666667v810.666666a21.333333 21.333333 0 0 1-21.333333 21.333334zM53.333333 341.333333a10.666667 10.666667 0 0 0-10.666666 10.666667v320a10.666667 10.666667 0 0 0 10.666666 10.666667h181.333334a21.333333 21.333333 0 0 1 15.086666 6.246666L426.666667 865.833333V158.166667L249.753333 335.086667A21.333333 21.333333 0 0 1 234.666667 341.333333z m964.42 377.753334a21.333333 21.333333 0 0 0 0-30.173334L840.833333 512l176.92-176.913333a21.333333 21.333333 0 1 0-30.173333-30.173334L810.666667 481.833333 633.753333 304.913333a21.333333 21.333333 0 0 0-30.173333 30.173334L780.5 512l-176.92 176.913333a21.333333 21.333333 0 0 0 30.173333 30.173334L810.666667 542.166667l176.913333 176.92a21.333333 21.333333 0 0 0 30.173333 0z"
153 fill="#0fd7ce"
154 p-id="1441"
155 />
156 </svg>
157 <input
158 v-if="volumeStatus"
159 type="range"
160 class="volume-range"
161 :value="volume"
162 max="100"
163 :style="{ backgroundSize: `${(volume / 100) * 100}% 100%` }"
164 @change="changeVolume"
165 />
166 </div>
167 </div>
168 </div>
169 </template>
170 <script lang="ts" setup>
171 import { onMounted, ref } from "vue";
172 const musicList: any[] = [
173 {
174 id: 26445261,
175 url: "https://apis.jxcxin.cn/api/kuwo?id=26445261&type=mp3",
176 icon: "https://img4.kuwo.cn/star/albumcover/500/12/73/1352603132.jpg",
177 singer: "买辣椒也用券",
178 song: "起风了(旧版)"
179 },
180 {
181 id: 60262165,
182 url: "https://apis.jxcxin.cn/api/kuwo?id=60262165&type=mp3",
183 icon: "https://img4.kuwo.cn/star/albumcover/500/28/0/866943227.jpg",
184 singer: "尚士达",
185 song: "生而为人"
186 },
187 {
188 id: 5961360,
189 url: "https://apis.jxcxin.cn/api/kuwo?id=5961360&type=mp3",
190 icon: "https://img1.kuwo.cn/star/albumcover/500/59/77/2376077807.jpg",
191 singer: "胡彦斌",
192 song: "月光"
193 },
194 {
195 id: 19519910,
196 url: "https://apis.jxcxin.cn/api/kuwo?id=19519910&type=mp3",
197 icon: "https://img1.kuwo.cn/star/albumcover/500/26/56/3241621283.jpg",
198 singer: "胡彦斌",
199 song: "诀别诗"
200 },
201 {
202 id: 158104409,
203 url: "https://apis.jxcxin.cn/api/kuwo?id=158104409&type=mp3",
204 icon: "https://img1.kuwo.cn/star/albumcover/500/58/72/3795397878.jpg",
205 singer: "蓝心羽",
206 song: "阿拉斯加海湾"
207 },
208 {
209 id: 178937138,
210 url: "https://apis.jxcxin.cn/api/kuwo?id=178937138&type=mp3",
211 icon: "https://img2.kuwo.cn/star/albumcover/500/60/49/2041460549.jpg",
212 singer: "LKer林柯",
213 song: "满目星辰皆是你"
214 }
215 ];
216 // 播放器实例
217 const audio = ref(null);
218 const playStatus = ref(false);
219 const currentIndex = ref(0);
220 let timer = null;
221 // 播放
222 const play = () => {
223 clearInterval(timer);
224 audio.value.play();
225 playStatus.value = true;
226 getMusicDuration();
227 currentTime.value = Math.floor(audio.value.currentTime) || 0;
228 timer = setInterval(() => {
229 currentTime.value++;
230 if (currentTime.value >= totalDuration.value) clearInterval(timer);
231 }, 1000);
232 };
233 // 暂停
234 const pause = () => {
235 audio.value.pause();
236 playStatus.value = false;
237 clearInterval(timer);
238 };
239
240 // 播放结束
241 const ended = () => {
242 if (currentIndex.value >= musicList.length - 1) {
243 currentIndex.value = 0;
244 return;
245 }
246 currentIndex.value++;
247 };
248 // 上一曲
249 const preMusic = () => {
250 currentTime.value = 0;
251 if (currentIndex.value === 0) {
252 currentIndex.value = musicList.length - 1;
253 play();
254 return;
255 }
256 currentIndex.value--;
257 play();
258 };
259 // 下一曲
260 const nextMusic = () => {
261 currentTime.value = 0;
262 if (currentIndex.value === musicList.length - 1) {
263 currentIndex.value = 0;
264 play();
265 return;
266 }
267 currentIndex.value++;
268 play();
269 };
270 // 拖动进度
271 const changePlayProgress = (event: any) => {
272 currentTime.value = event.target.value;
273 audio.value.currentTime = currentTime.value;
274 };
275 // 静音
276 const muteStatus = ref(false);
277 const volume = ref(10);
278 const volumeStatus = ref(false);
279 const changeVolume = (e: any) => {
280 volume.value = e.target.value;
281 volume.value == 0 ? (muteStatus.value = true) : (muteStatus.value = false);
282 audio.value.volume = volume.value / 100;
283 volumeStatus.value = false;
284 };
285 // duration
286 const currentTime = ref(0);
287 const totalDuration = ref(100);
288 const getMusicDuration = () => {
289 if (![2, 3, 4].includes(audio.value.readyState)) return;
290 setTimeout(() => {
291 totalDuration.value = Math.floor(audio.value.duration);
292 }, 2000);
293 };
294 // 初始化
295 onMounted(() => {
296 getMusicDuration();
297 audio.value.volume = volume.value / 100;
298 audio.value.addEventListener("play", () => {
299 play();
300 });
301 });
302 </script>
303 <style scoped>
304 .wrapper {
305 display: flex;
306 align-items: center;
307 border-radius: 10px;
308 width: 460px;
309 background-image: linear-gradient(to right, white, #63ff63);
310 margin-bottom: 300px;
311 }
312 .wrapper .rotateIcon {
313 width: 20px;
314 height: 40px;
315 transform: translateY(-40px) translateX(18px);
316 }
317 .icon {
318 width: 20px;
319 height: 40px;
320 }
321 .musicIcon {
322 width: 120px;
323 height: 120px;
324 border-radius: 50%;
325 border: 4px solid white;
326 animation: rotateIcon 6s linear infinite backwards;
327 }
328 @keyframes rotateIcon {
329 from {
330 transform: rotate(0deg);
331 }
332 to {
333 transform: rotate(360deg);
334 }
335 }
336 .controls {
337 display: flex;
338 flex: 1;
339 flex-direction: column;
340 align-items: center;
341 padding: 20px;
342 }
343 .controls .controls-title {
344 color: #348eb1;
345 font-size: 12px;
346 }
347 .controls .controls-change {
348 display: flex;
349 justify-content: space-between;
350 margin-top: 10px;
351 width: 100%;
352 }
353 .controls .controls-change .icon {
354 width: 30px;
355 cursor: pointer;
356 }
357 .controls .controls-progress {
358 display: flex;
359 width: 100%;
360 align-items: center;
361 font-size: 12px;
362 position: relative;
363 }
364 .controls .controls-progress .controls-progress-duration {
365 color: #0f9cd3;
366 }
367 .controls .controls-progress .controls-progress-range {
368 flex: 1;
369 margin: 0 20px;
370 outline: none;
371 -webkit-appearance: none;
372 background: -webkit-linear-gradient(#cc0ceb, #dc01ff) no-repeat, #ddd;
373 height: 3px;
374 }
375 .controls .controls-progress .controls-progress-range::-webkit-slider-thumb {
376 -webkit-appearance: none;
377 height: 16px;
378 width: 16px;
379 background: #f8f9fa;
380 border-radius: 50%;
381 border: solid 1px #ddd;
382 }
383 .controls .controls-progress .icon {
384 cursor: pointer;
385 }
386 .volume-range {
387 position: absolute;
388 right: 0;
389 transform: rotate(-90deg) translate(40px, 18px);
390 outline: none;
391 -webkit-appearance: none;
392 background: -webkit-linear-gradient(#d5601c, #ff6308) no-repeat, #ddd;
393 height: 3px;
394 width: 60px;
395 }
396 .controls .controls-progress .volume-range::-webkit-slider-thumb {
397 -webkit-appearance: none;
398 height: 14px;
399 width: 4px;
400 background: #f8f9fa;
401 border-radius: 50%;
402 border: solid 1px #ddd;
403 }
404 </style>
展开
手写一个audio播放器,实现歌曲切换,列表歌曲循环,音量调节等 vue组件的更多相关文章
- 用 EPWA 写一个 图片播放器 PicturePlayer
用 EPWA 写一个 图片播放器 PicturePlayer . 有关 EPWA,见 <我发起并创立了一个 EPWA 的 开源项目> https://www.cnblogs.com ...
- 从零开始学习PYTHON3讲义(十四)写一个mp3播放器
<从零开始PYTHON3>第十四讲 通常来说,Python解释执行,运行速度慢,并不适合完整的开发游戏.随着电脑速度的快速提高,这种情况有所好转,但开发游戏仍然不是Python的重点工作. ...
- 师兄写的一个JAVA播放器的源代码(转)
师兄写的一个JAVA播放器的源代码 MediaPlayer.java------------------------------------------------------------------ ...
- 大半宿,封装了一个MP3播放器的类,写了个简陋的播放器
用 winmm.lib 写的 封装不是很好,而且没有优化,效率可能有问题,但是现在几乎没有什么大问题 我用我封装的类,写了一个小播放器,界面上的所有功能都实现了,包括双击列表中的文件名,直接播放文件 ...
- python 拼写检查代码(怎样写一个拼写检查器)
原文:http://norvig.com/spell-correct.html 翻译:http://blog.youxu.info/spell-correct.html 怎样写一个拼写检查器 Pete ...
- 利用SpringBoot+Logback手写一个简单的链路追踪
目录 一.实现原理 二.代码实战 三.测试 最近线上排查问题时候,发现请求太多导致日志错综复杂,没办法把用户在一次或多次请求的日志关联在一起,所以就利用SpringBoot+Logback手写了一个简 ...
- 手写一个简单的ElasticSearch SQL转换器(一)
一.前言 之前有个需求,是使ElasticSearch支持使用SQL进行简单查询,较新版本的ES已经支持该特性(不过貌似还是实验性质的?) ,而且git上也有elasticsearch-sql 插件, ...
- 看年薪50W的架构师如何手写一个SpringMVC框架
前言 做 Java Web 开发的你,一定听说过SpringMVC的大名,作为现在运用最广泛的Java框架,它到目前为止依然保持着强大的活力和广泛的用户群. 本文介绍如何用eclipse一步一步搭建S ...
- 吴裕雄--天生自然python学习笔记:python 用pygame模块制作一个音效播放器
用 Sound 对象制作一个音效播放器. 应用程序总览 程序在执行后默认会把 WAV 音频文件加载到清单中,单击“播放”按钮可开始 播放,同时显示 “正在播放 xxx 音效”的信息 . 播放过程中,可 ...
- 剖析手写Vue,你也可以手写一个MVVM框架
剖析手写Vue,你也可以手写一个MVVM框架# 邮箱:563995050@qq.com github: https://github.com/xiaoqiuxiong 作者:肖秋雄(eddy) 温馨提 ...
随机推荐
- kubernetes笔记-3-快速入门
一.增删改查 root@master:~# kubectl run ninig-deploy --image=nginx:1.14-alpine --port=80 --replicas=1 --dr ...
- Vscode连接gitee远程仓库
Git初始化项目 1. Git的基础配置 Git的安装配置 下载地址为:http://git-scm.com/downloads 安装完第一步要做的是,设置你的用户名和邮件地址. git config ...
- Xamarin.Android带参数返回上一级界面
在ActivityA跳转到ActivityB后.activityB返回到ActivityA并带参数返回 首先再activitya中跳转到b var intent = new Intent(this, ...
- PyQt5程序打包出错Failed to execute script
出现这种报错一般有两种可能: 1. 该被引入的资源你没有放到 exe 的相对路径 这种错误一般是你直接引入图片或者图标, 而没有放到 打包后的exe的相对路径 2. 加参数 假设 main.py 为程 ...
- Dojo dijit/Tree的使用以及样式设置
如果什么都不设置,默认使用dojo自带的Tree,样式模板使用claro的,效果是这样的. 1.无论是不是叶子节点,前面总要带个+号,必须要点击下才消失. 2.点击树或者某个节点是,回出现蓝色边框. ...
- day 26 form表单标签 & CSS样式表-选择器 & 样式:背景、字体、定位等
html常用标签 嵌套页面 <!-- 嵌套页面 --> <div> <!-- target属性值可以通过指定的iframe的name属性值, 实现超链接页面,在嵌套页面展 ...
- day14 I/O流——序列化与反序列化 & 计算机网络五层架构 & TCP的建立连接与断开连接
day 14 序列化与反序列化 序列化 将对象转化成特定格式的字符串文件(字节文件)叫做序列化 1.一个类要想实现序列化,必须实现serializable接口 2.序列化用途 1)把对象的字节序列 ...
- js day04 综合案例秒数计算
<script> //用户输入总秒数 let second = +prompt('请输入总秒数:') //计算时分秒 fun ...
- 【微服务架构设计实施】第一部分:架构篇-1:微服务架构与Spring Cloud介绍
〇.概述 一.微服务架构与Spring Cloud (一)概念 不同说法:细粒度的.清凉组件化的小型SOA(面向服务架构) 统一说法:小型应用程序(服务组件),使用轻量级设计方法和HTTP协议通信 理 ...
- LoadRunner11录制脚本
1.打开LoadRunner11后界面如下: 2.点击"创建/编辑脚本",会打开一个新窗口,如下: 3.这里新建一个web/html格式的测试.点击"文件"-& ...