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组件的更多相关文章

  1. 用 EPWA 写一个 图片播放器 PicturePlayer

    用 EPWA 写一个 图片播放器  PicturePlayer  . 有关 EPWA,见 <我发起并创立了一个 EPWA 的 开源项目>   https://www.cnblogs.com ...

  2. 从零开始学习PYTHON3讲义(十四)写一个mp3播放器

    <从零开始PYTHON3>第十四讲 通常来说,Python解释执行,运行速度慢,并不适合完整的开发游戏.随着电脑速度的快速提高,这种情况有所好转,但开发游戏仍然不是Python的重点工作. ...

  3. 师兄写的一个JAVA播放器的源代码(转)

    师兄写的一个JAVA播放器的源代码 MediaPlayer.java------------------------------------------------------------------ ...

  4. 大半宿,封装了一个MP3播放器的类,写了个简陋的播放器

    用 winmm.lib 写的 封装不是很好,而且没有优化,效率可能有问题,但是现在几乎没有什么大问题 我用我封装的类,写了一个小播放器,界面上的所有功能都实现了,包括双击列表中的文件名,直接播放文件 ...

  5. python 拼写检查代码(怎样写一个拼写检查器)

    原文:http://norvig.com/spell-correct.html 翻译:http://blog.youxu.info/spell-correct.html 怎样写一个拼写检查器 Pete ...

  6. 利用SpringBoot+Logback手写一个简单的链路追踪

    目录 一.实现原理 二.代码实战 三.测试 最近线上排查问题时候,发现请求太多导致日志错综复杂,没办法把用户在一次或多次请求的日志关联在一起,所以就利用SpringBoot+Logback手写了一个简 ...

  7. 手写一个简单的ElasticSearch SQL转换器(一)

    一.前言 之前有个需求,是使ElasticSearch支持使用SQL进行简单查询,较新版本的ES已经支持该特性(不过貌似还是实验性质的?) ,而且git上也有elasticsearch-sql 插件, ...

  8. 看年薪50W的架构师如何手写一个SpringMVC框架

    前言 做 Java Web 开发的你,一定听说过SpringMVC的大名,作为现在运用最广泛的Java框架,它到目前为止依然保持着强大的活力和广泛的用户群. 本文介绍如何用eclipse一步一步搭建S ...

  9. 吴裕雄--天生自然python学习笔记:python 用pygame模块制作一个音效播放器

    用 Sound 对象制作一个音效播放器. 应用程序总览 程序在执行后默认会把 WAV 音频文件加载到清单中,单击“播放”按钮可开始 播放,同时显示 “正在播放 xxx 音效”的信息 . 播放过程中,可 ...

  10. 剖析手写Vue,你也可以手写一个MVVM框架

    剖析手写Vue,你也可以手写一个MVVM框架# 邮箱:563995050@qq.com github: https://github.com/xiaoqiuxiong 作者:肖秋雄(eddy) 温馨提 ...

随机推荐

  1. day01-计算机的本质

    计算机的本质 计算机又称为"电脑": 通电的大脑 意味着我们人类希望计算机通电之后可以跟人脑一样思考问题.解决问题 计算机存储数据的本质 计算机是基于电工作,而电信号只有高低电平两 ...

  2. 进军东南亚市场,腾讯云数据库 TDSQL 助力印尼 BNC 银行数字化转型

    腾讯云数据库在助力金融核心系统分布式替换上,已经辐射到了东南亚市场. 东南亚最大的银行之一印尼BNC银行(Bank Neo Commerce)已正式完成新核心分布式迁移,使用腾讯云数据库TDSQL后, ...

  3. 设计链表-LeetCode707 基础题

    LeetCode链接:https://leetcode.cn/problems/design-linked-list/ 题目:设计链表的实现.您可以选择使用单链表或双链表.单链表中的节点应该具有两个属 ...

  4. Relational Learning with Gated and Attentive Neighbor Aggregator for Few-Shot Knowledge Graph Completion 小样本关系学习论文解读

    小样本知识图补全--关系学习.利用三元组的邻域信息,提升模型的关系表示学习,来实现小样本的链接预测.主要应用的思想和模型包括:GAT.TransH.SLTM.Model-Agnostic Meta-L ...

  5. 回溯法求解n皇后问题(复习)

    回溯法 回溯法是最常用的解题方法,有"通用的解题法"之称.当要解决的问题有若干可行解时,则可以在包含问题所有解的空间树中,按深度优先的策略,从根节点出发搜索解空间树.算法搜索至解空 ...

  6. 《HTTP权威指南》– 3.HTTP方法和状态码

    常见HTTP方法: 常用HTTP方法 描述 是否包含主体 GET 从服务器获取一份文档 否 HEAD 只从服务器获取文档的首部 否 POST 向服务器发送需要处理的数据 是 PUT 将请求的主体部分存 ...

  7. DTMF2num拨号音识别

    说明 很多出题人可能会把手机或者其他设备打电话的拨号音作为一个题目技能中的考察点. 什么是DTMF? 双音多频的拨号键盘是4×4的矩阵,每一行代表一个低频,每一列代表一个高频.每按一个键就发送一个高频 ...

  8. [MySQL] 索引的使用、SQL语句优化策略

    目录 索引 什么是索引 索引的创建与删除 创建索引 删除索引 索引的使用 使用explain分析SQL语句 最佳左前缀 索引覆盖 避免对索引列进行额外运算 SQL语句优化 小表驱动大表 索引 什么是索 ...

  9. 轻松理解Promise.all 、Promise.then、Promise.race有什么区别以及使用方法

    简单来说呢,Promse.all一般应用于某个场景需要多个接口数据合并起来才能实现 有个极大地好处我必须说一下,请求顺序和获取数据顺序是一样的哟,大可放心使用~~ const success1 = n ...

  10. CF1051E Vasya and Big Integers

    [CF1051E Vasya and Big Integers](Problem - E - Codeforces) sb的做法 单调队列乱整( #include<bits/stdc++.h&g ...