├── LICENSE
├── README.md
├── assets
├── css
│ └── style.css
├── images
│ ├── Thumbs.db
│ ├── poster-1.jpg
│ ├── poster-2.jpg
│ ├── poster-3.jpg
│ ├── poster-4.jpg
│ └── poster-5.jpg
├── js
│ └── script.js
└── music
│ ├── music-1.mp3
│ ├── music-2.mp3
│ ├── music-3.mp3
│ ├── music-4.mp3
│ └── music-5.mp3
├── favicon.svg
├── index.html
├── index.txt
├── readme-images
└── desktop.png
└── style-guide.md
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Sadee
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | 
4 | 
5 | 
6 | [](https://twitter.com/intent/follow?screen_name=codewithsadee_)
7 | [](https://youtu.be/jbMd2NVFrZk)
8 |
9 |
10 |
11 |
12 |
Web Music Player
13 |
14 | A fully responsive web music player using vanilla javascript,
Responsive for all devices, build using html, css, and javascript.
15 |
16 |
➥ Live Demo
17 |
18 |
19 |
20 |
21 |
22 | ### Demo Screeshots
23 |
24 | 
25 |
26 | ### Prerequisites
27 |
28 | Before you begin, ensure you have met the following requirements:
29 |
30 | * [Git](https://git-scm.com/downloads "Download Git") must be installed on your operating system.
31 |
32 | ### Run Locally
33 |
34 | To run **Music Player** locally, run this command on your git bash:
35 |
36 | Linux and macOS:
37 |
38 | ```bash
39 | sudo git clone https://github.com/codewithsadee/music-player.git
40 | ```
41 |
42 | Windows:
43 |
44 | ```bash
45 | git clone https://github.com/codewithsadee/music-player.git
46 | ```
47 |
48 | ### Contact
49 |
50 | If you want to contact with me you can reach me at [Twitter](https://www.twitter.com/codewithsadee).
51 |
52 | ### License
53 |
54 | This project is **free to use** and does not contains any license.
55 |
--------------------------------------------------------------------------------
/assets/css/style.css:
--------------------------------------------------------------------------------
1 | /*-----------------------------------*\
2 | #style.css
3 | \*-----------------------------------*/
4 |
5 | /**
6 | * copyright 2022 codewithsadee
7 | */
8 |
9 |
10 |
11 |
12 |
13 | /*-----------------------------------*\
14 | #CUSTOM PROPERTY
15 | \*-----------------------------------*/
16 |
17 | :root {
18 |
19 | /**
20 | * colors
21 | */
22 |
23 | --eerie-black_a95: hsla(204, 9%, 11%, 0.95);
24 | --eerie-black_a50: hsla(204, 9%, 11%, 0.5);
25 | --eerie-black: hsl(204, 9%, 11%);
26 | --gainsboro: hsl(225, 7%, 89%);
27 | --charcoal: hsl(203, 9%, 28%);
28 | --silver-sand: hsl(208, 12%, 78%);
29 | --light-sky-blue: hsl(200, 100%, 73%);
30 | --prussian-blue: hsl(196, 100%, 14%);
31 | --black: hsl(0, 0%, 0%);
32 | --black_a50: hsla(0, 0%, 0%, 0.5);
33 | --black_a20: hsla(0, 0%, 0%, 0.2);
34 | --light-sky-blue_a8: hsla(200, 100%, 73%, 0.08);
35 | --light-sky-blue_a12: hsla(200, 100%, 73%, 0.12);
36 | --silver-sand_a8: hsla(208, 12%, 78%, 0.08);
37 | --silver-sand_a12: hsla(208, 12%, 78%, 0.12);
38 |
39 | --background: var(--eerie-black);
40 | --background-opacity: var(--eerie-black_a95);
41 | --on-background: var(--gainsboro);
42 | --surface-variant: var(--charcoal);
43 | --on-surface-variant: var(--silver-sand);
44 | --on-surface-variant-hover: var(--light-sky-blue_a8);
45 | --on-surface-variant-focus: var(--light-sky-blue_a12);
46 | --primary: var(--light-sky-blue);
47 | --on-primary: var(--prussian-blue);
48 |
49 | /**
50 | * gradient
51 | */
52 |
53 | --gradient: linear-gradient(180deg, hsla(204, 9%, 11%, 0.90) 60%, transparent 120%);
54 |
55 | /**
56 | * typography
57 | */
58 |
59 | --ff-inter: 'Inter', sans-serif;
60 |
61 | --headline-sm: 2.4rem;
62 | --title-lg: 2.2rem;
63 | --label-lg: 1.4rem;
64 | --label-md: 1.2rem;
65 |
66 | --fw-400: 400;
67 | --fw-500: 500;
68 |
69 | /**
70 | * box shadow
71 | */
72 |
73 | --shadow-1: 0 1px 4px 1px var(--black_a20);
74 | --shadow-2: 0 1px 4px 1px var(--black_a50);
75 |
76 | /**
77 | * border radius
78 | */
79 |
80 | --radius-24: 24px;
81 | --radius-16: 16px;
82 | --radius-12: 12px;
83 | --radius-pill: 100px;
84 | --radius-circle: 50%;
85 |
86 | /**
87 | * transition
88 | */
89 |
90 | --transition-1: 200ms cubic-bezier(0.2, 0, 0, 1);
91 | --transition-2: 300ms cubic-bezier(0.2, 0, 0, 1);
92 |
93 | }
94 |
95 |
96 |
97 |
98 |
99 | /*-----------------------------------*\
100 | #RESET
101 | \*-----------------------------------*/
102 |
103 | *,
104 | *::before,
105 | *::after {
106 | margin: 0;
107 | padding: 0;
108 | box-sizing: border-box;
109 | }
110 |
111 | li { list-style: none; }
112 |
113 | a,
114 | img,
115 | span,
116 | input,
117 | button { display: block; }
118 |
119 | a {
120 | text-decoration: none;
121 | color: inherit;
122 | }
123 |
124 | img { height: auto; }
125 |
126 | input,
127 | button {
128 | background: none;
129 | border: none;
130 | font: inherit;
131 | }
132 |
133 | input { width: 100%; }
134 |
135 | button { cursor: pointer; }
136 |
137 | html {
138 | font-family: var(--ff-inter);
139 | font-size: 10px;
140 | scroll-behavior: smooth;
141 | }
142 |
143 | body {
144 | background-color: var(--black);
145 | color: var(--on-background);
146 | font-size: 1.6rem;
147 | line-height: 1.5;
148 | min-height: 100vh;
149 | min-width: 250px;
150 | background-image: url("../images/poster-1.jpg");
151 | background-size: 150%;
152 | background-repeat: no-repeat;
153 | background-position: top;
154 | overflow: overlay;
155 | }
156 |
157 | body.modalActive { overflow: hidden; }
158 |
159 | ::-webkit-scrollbar { width: 8px; }
160 |
161 | ::-webkit-scrollbar-track { background: transparent; }
162 |
163 | ::-webkit-scrollbar-thumb {
164 | background-color: transparent;
165 | border-radius: var(--radius-pill);
166 | }
167 |
168 | body:hover::-webkit-scrollbar-thumb { background-color: var(--surface-variant); }
169 |
170 |
171 |
172 |
173 |
174 | /*-----------------------------------*\
175 | #REUSED STYLE
176 | \*-----------------------------------*/
177 |
178 | .material-symbols-rounded {
179 | font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' -25, 'opsz' 24;
180 | width: 1em;
181 | height: 1em;
182 | overflow: hidden;
183 | user-select: none;
184 | }
185 |
186 | .wrapper {
187 | display: flex;
188 | align-items: center;
189 | }
190 |
191 | .title-lg {
192 | font-size: var(--title-lg);
193 | font-weight: var(--fw-400);
194 | }
195 |
196 | .btn-icon {
197 | color: var(--on-surface-variant);
198 | width: 40px;
199 | height: 40px;
200 | display: grid;
201 | place-items: center;
202 | border-radius: var(--radius-circle);
203 | }
204 |
205 | .btn-icon:hover { background-color: var(--on-surface-variant-hover); }
206 |
207 | .btn-icon:is(:focus, :focus-visible) {
208 | background-color: var(--on-surface-variant-focus);
209 | }
210 |
211 | .img-cover {
212 | width: 100%;
213 | height: 100%;
214 | object-fit: cover;
215 | }
216 |
217 | .headline-sm {
218 | font-size: var(--headline-sm);
219 | font-weight: var(--fw-400);
220 | }
221 |
222 | .label-lg,
223 | .label-md { font-weight: var(--fw-500); }
224 |
225 | .label-lg {
226 | font-size: var(--label-lg);
227 | letter-spacing: 0.1px;
228 | }
229 |
230 | .label-md {
231 | font-size: var(--label-md);
232 | letter-spacing: 0.5px;
233 | }
234 |
235 |
236 |
237 |
238 |
239 | /*-----------------------------------*\
240 | #TOP APP BAR
241 | \*-----------------------------------*/
242 |
243 | .top-bar {
244 | position: fixed;
245 | top: 0;
246 | left: 0;
247 | background-color: var(--background-opacity);
248 | min-width: 250px;
249 | width: 100%;
250 | height: 64px;
251 | padding-inline: 16px;
252 | justify-content: space-between;
253 | backdrop-filter: blur(50px);
254 | box-shadow: var(--shadow-1);
255 | z-index: 2;
256 | }
257 |
258 | .logo { gap: 12px; }
259 |
260 |
261 |
262 |
263 |
264 | /*-----------------------------------*\
265 | #PLAYER
266 | \*-----------------------------------*/
267 |
268 | .volume { display: none; }
269 |
270 | .player {
271 | --padding: 24px;
272 | background-image: var(--gradient);
273 | padding: var(--padding);
274 | padding-block-start: calc(64px + var(--padding));
275 | min-height: 100vh;
276 | display: flex;
277 | text-align: center;
278 | backdrop-filter: blur(30px);
279 | overflow: hidden;
280 | }
281 |
282 | .player .container {
283 | width: 100%;
284 | display: grid;
285 | grid-template-rows: 1fr max-content;
286 | gap: 24px;
287 | }
288 |
289 | .music-banner {
290 | aspect-ratio: 1 / 1;
291 | background-color: var(--surface-variant);
292 | max-width: 400px;
293 | margin-inline: auto;
294 | align-self: center;
295 | overflow: hidden;
296 | border-radius: var(--radius-24);
297 | }
298 |
299 | .label-wrapper {
300 | justify-content: center;
301 | opacity: 0.8;
302 | margin-block: 8px 4px;
303 | }
304 |
305 | .label-wrapper span:first-child::after {
306 | content: "|";
307 | display: inline-block;
308 | margin-inline: 4px;
309 | }
310 |
311 | .artist {
312 | opacity: 0.6;
313 | margin-block-end: 36px;
314 | }
315 |
316 | .seek-control { margin-block-end: 20px; }
317 |
318 | .range-wrapper { position: relative; }
319 |
320 | .range {
321 | appearance: none;
322 | cursor: pointer;
323 | }
324 |
325 | .range::-webkit-slider-runnable-track {
326 | appearance: none;
327 | background-color: var(--surface-variant);
328 | height: 6px;
329 | width: 100%;
330 | border-radius: var(--radius-pill);
331 | }
332 |
333 | .range-fill {
334 | content: "";
335 | position: absolute;
336 | top: 0;
337 | left: 0;
338 | width: 0;
339 | height: 6px;
340 | background-color: var(--primary);
341 | border-radius: var(--radius-pill);
342 | pointer-events: none;
343 | }
344 |
345 | .range::-webkit-slider-thumb {
346 | appearance: none;
347 | background-color: var(--primary);
348 | width: 16px;
349 | height: 16px;
350 | margin-block-start: -5px;
351 | border-radius: var(--radius-pill);
352 | transition: var(--transition-1);
353 | }
354 |
355 | .range::-webkit-slider-thumb:hover {
356 | box-shadow: 0 0 0 12px var(--on-surface-variant-hover);
357 | }
358 |
359 | .range::-webkit-slider-thumb:is(:focus, :focus-visible) {
360 | box-shadow: 0 0 0 12px var(--on-surface-variant-focus);
361 | }
362 |
363 | .duration-label {
364 | justify-content: space-between;
365 | margin-block-start: 12px;
366 | }
367 |
368 | .player-control { justify-content: space-evenly; }
369 |
370 | .player-control .toggle.active { color: var(--primary); }
371 |
372 | .player-control .play {
373 | background-color: var(--surface-variant);
374 | color: var(--primary);
375 | }
376 |
377 | .player-control .play.active {
378 | background-color: var(--primary);
379 | color: var(--on-primary);
380 | }
381 |
382 | .player-control .btn-icon.active .default-icon,
383 | .player-control .btn-icon .active-icon { display: none; }
384 |
385 | .player-control .btn-icon .default-icon,
386 | .player-control .btn-icon.active .active-icon { display: block; }
387 |
388 |
389 |
390 |
391 |
392 | /*-----------------------------------*\
393 | #PLAYLIST
394 | \*-----------------------------------*/
395 |
396 | .playlist {
397 | position: fixed;
398 | top: 0;
399 | right: -180px;
400 | width: 180px;
401 | height: 100vh;
402 | background-color: var(--background);
403 | padding: 28px;
404 | border-top-left-radius: var(--radius-16);
405 | border-bottom-left-radius: var(--radius-16);
406 | box-shadow: var(--shadow-2);
407 | overflow-y: overlay;
408 | visibility: hidden;
409 | transition: var(--transition-2);
410 | z-index: 4;
411 | }
412 |
413 | .playlist:hover::-webkit-scrollbar-thumb { background-color: var(--surface-variant); }
414 |
415 | .playlist.active {
416 | transform: translateX(-180px);
417 | visibility: visible;
418 | }
419 |
420 | .music-list {
421 | display: grid;
422 | gap: 20px;
423 | }
424 |
425 | .music-item {
426 | position: relative;
427 | border-radius: var(--radius-12);
428 | background-color: var(--surface-variant);
429 | overflow: hidden;
430 | }
431 |
432 | .music-item .item-icon {
433 | position: absolute;
434 | inset: 0;
435 | background-color: var(--eerie-black_a50);
436 | display: grid;
437 | place-items: center;
438 | opacity: 0;
439 | }
440 |
441 | .music-item.playing .item-icon { opacity: 1; }
442 |
443 | .music-item .item-icon .material-symbols-rounded {
444 | color: var(--primary);
445 | font-size: 3rem;
446 | }
447 |
448 | .music-item:is(:hover, :focus-visible, :active, .playing) {
449 | outline: 1px solid var(--primary);
450 | }
451 |
452 | .overlay {
453 | position: fixed;
454 | top: 0;
455 | left: 0;
456 | width: 100%;
457 | height: 100vh;
458 | background-color: var(--black);
459 | opacity: 0;
460 | pointer-events: none;
461 | transition: var(--transition-2);
462 | z-index: 3;
463 | }
464 |
465 | .overlay.active {
466 | pointer-events: all;
467 | opacity: 0.5;
468 | }
469 |
470 |
471 |
472 |
473 |
474 | /*-----------------------------------*\
475 | #MEDIA QUERIES
476 | \*-----------------------------------*/
477 |
478 | /**
479 | * responsive for large than 575px screen
480 | */
481 |
482 | @media (min-width: 575px) {
483 |
484 | /**
485 | * RESET
486 | */
487 |
488 | body { background-size: 100%; }
489 |
490 |
491 |
492 | /**
493 | * PLAYER
494 | */
495 |
496 | .player { justify-content: center; }
497 |
498 | .player .container { max-width: 540px; }
499 |
500 | }
501 |
502 |
503 |
504 |
505 |
506 | /**
507 | * responsive for large than 992px screen
508 | */
509 |
510 | @media (min-width: 992px) {
511 |
512 | /**
513 | * CUSTOM PROPERTY
514 | */
515 |
516 | :root {
517 |
518 | /**
519 | * typography
520 | */
521 |
522 | --headline-sm: 4.2rem;
523 | --label-lg: 2.2rem;
524 |
525 | }
526 |
527 |
528 |
529 | /**
530 | * RESET
531 | */
532 |
533 | body {
534 | background-size: 40% 100%;
535 | background-position: left center;
536 | }
537 |
538 | article { display: flex; }
539 |
540 |
541 |
542 | /**
543 | * REUSED STYLE
544 | */
545 |
546 | .btn-icon {
547 | width: 54px;
548 | height: 54px;
549 | }
550 |
551 | .btn-icon .material-symbols-rounded { font-size: 2.8rem; }
552 |
553 |
554 |
555 | /**
556 | * TOP APP BAR
557 | */
558 |
559 | .top-bar-actions { display: none; }
560 |
561 |
562 |
563 | /**
564 | * PLAYER
565 | */
566 |
567 | .player {
568 | --padding: 48px;
569 | text-align: left;
570 | flex-grow: 1;
571 | align-items: center;
572 | backdrop-filter: blur(100px);
573 | }
574 |
575 | .player .container {
576 | max-width: 1200px;
577 | grid-template-columns: 0.8fr 1fr;
578 | grid-template-rows: 1fr;
579 | gap: 48px;
580 | max-height: 500px;
581 | height: 100%;
582 | }
583 |
584 | .music-banner {
585 | aspect-ratio: unset;
586 | max-width: max-content;
587 | width: 100%;
588 | height: 100%;
589 | }
590 |
591 | .music-content {
592 | display: flex;
593 | flex-direction: column;
594 | padding-block-start: 48px;
595 | min-width: 100%;
596 | }
597 |
598 | .music-content :is(.headline-sm, .label-lg, .label-md) {
599 | max-width: 85%;
600 | }
601 |
602 | .label-wrapper {
603 | justify-content: flex-start;
604 | margin-block-end: 8px;
605 | }
606 |
607 | .artist { --label-md: 1.8rem; }
608 |
609 | .seek-control {
610 | margin-block-start: auto;
611 | display: grid;
612 | grid-template-columns: 1fr 150px;
613 | align-items: center;
614 | gap: 24px;
615 | }
616 |
617 | .volume {
618 | display: flex;
619 | align-items: center;
620 | gap: 4px;
621 | margin-block-start: -30px;
622 | }
623 |
624 | .volume .btn-icon { flex-shrink: 0; }
625 |
626 | .volume .range-fill { width: 100%; }
627 |
628 | .player-control {
629 | margin-inline-end: 174px;
630 | min-width: max-content;
631 | gap: 8px;
632 | }
633 |
634 |
635 |
636 | /**
637 | * PLAYLIST
638 | */
639 |
640 | .overlay { display: none; }
641 |
642 | .playlist {
643 | position: static;
644 | visibility: visible;
645 | border-radius: 0;
646 | box-shadow: none;
647 | flex-shrink: 0;
648 | }
649 |
650 | .playlist.active { transform: unset; }
651 |
652 | .music-item:is(:hover, :focus-visible, :active, .playing) {
653 | outline: 2px solid var(--primary);
654 | }
655 |
656 | .music-item .item-icon .material-symbols-rounded {
657 | font-size: 3.6rem;
658 | }
659 |
660 | }
661 |
662 |
663 |
664 |
665 |
666 | /**
667 | * responsive for large than 1200px screen
668 | */
669 |
670 | @media (min-width: 1200px) {
671 |
672 | /**
673 | * PLAYLIST
674 | */
675 |
676 | .playlist {
677 | padding: 32px;
678 | width: 220px;
679 | }
680 |
681 | .music-list { gap: 28px; }
682 |
683 | }
--------------------------------------------------------------------------------
/assets/images/Thumbs.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codewithsadee/music-player/1e1ffb4d6fad2cea6ba3e604ccddcc0d68f3dc4f/assets/images/Thumbs.db
--------------------------------------------------------------------------------
/assets/images/poster-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codewithsadee/music-player/1e1ffb4d6fad2cea6ba3e604ccddcc0d68f3dc4f/assets/images/poster-1.jpg
--------------------------------------------------------------------------------
/assets/images/poster-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codewithsadee/music-player/1e1ffb4d6fad2cea6ba3e604ccddcc0d68f3dc4f/assets/images/poster-2.jpg
--------------------------------------------------------------------------------
/assets/images/poster-3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codewithsadee/music-player/1e1ffb4d6fad2cea6ba3e604ccddcc0d68f3dc4f/assets/images/poster-3.jpg
--------------------------------------------------------------------------------
/assets/images/poster-4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codewithsadee/music-player/1e1ffb4d6fad2cea6ba3e604ccddcc0d68f3dc4f/assets/images/poster-4.jpg
--------------------------------------------------------------------------------
/assets/images/poster-5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codewithsadee/music-player/1e1ffb4d6fad2cea6ba3e604ccddcc0d68f3dc4f/assets/images/poster-5.jpg
--------------------------------------------------------------------------------
/assets/js/script.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 |
4 |
5 | /**
6 | * all music information
7 | */
8 |
9 | const musicData = [
10 | {
11 | backgroundImage: "./assets/images/poster-1.jpg",
12 | posterUrl: "./assets/images/poster-1.jpg",
13 | title: "Happy Moments (Master)",
14 | album: "No Spirit",
15 | year: 2022,
16 | artist: "No Spirit x Tonion",
17 | musicPath: "./assets/music/music-1.mp3",
18 | },
19 | {
20 | backgroundImage: "./assets/images/poster-2.jpg",
21 | posterUrl: "./assets/images/poster-2.jpg",
22 | title: "We Are Going To Be Ok (Master)",
23 | album: "No Spirit",
24 | year: 2022,
25 | artist: "No Spirit x Jhove",
26 | musicPath: "./assets/music/music-2.mp3",
27 | },
28 | {
29 | backgroundImage: "./assets/images/poster-3.jpg",
30 | posterUrl: "./assets/images/poster-3.jpg",
31 | title: "Winter Meadow",
32 | album: "No Spirit",
33 | year: 2022,
34 | artist: "No Spirit x juniorodeo",
35 | musicPath: "./assets/music/music-3.mp3",
36 | },
37 | {
38 | backgroundImage: "./assets/images/poster-4.jpg",
39 | posterUrl: "./assets/images/poster-4.jpg",
40 | title: "From Where We Started",
41 | album: "No Spirit",
42 | year: 2022,
43 | artist: "No Spirit",
44 | musicPath: "./assets/music/music-4.mp3",
45 | },
46 | {
47 | backgroundImage: "./assets/images/poster-5.jpg",
48 | posterUrl: "./assets/images/poster-5.jpg",
49 | title: "Where I Found You",
50 | album: "No Spirit",
51 | year: 2022,
52 | artist: "No Spirit",
53 | musicPath: "./assets/music/music-5.mp3",
54 | },
55 | ];
56 |
57 |
58 |
59 | /**
60 | * add eventListnere on all elements that are passed
61 | */
62 |
63 | const addEventOnElements = function (elements, eventType, callback) {
64 | for (let i = 0, len = elements.length; i < len; i++) {
65 | elements[i].addEventListener(eventType, callback);
66 | }
67 | }
68 |
69 |
70 |
71 | /**
72 | * PLAYLIST
73 | *
74 | * add all music in playlist, from 'musicData'
75 | */
76 |
77 | const playlist = document.querySelector("[data-music-list]");
78 |
79 | for (let i = 0, len = musicData.length; i < len; i++) {
80 | playlist.innerHTML += `
81 |
82 |
83 |
85 |
86 |
87 | equalizer
88 |
89 |
90 |
91 | `;
92 | }
93 |
94 |
95 |
96 | /**
97 | * PLAYLIST MODAL SIDEBAR TOGGLE
98 | *
99 | * show 'playlist' modal sidebar when click on playlist button in top app bar
100 | * and hide when click on overlay or any playlist-item
101 | */
102 |
103 | const playlistSideModal = document.querySelector("[data-playlist]");
104 | const playlistTogglers = document.querySelectorAll("[data-playlist-toggler]");
105 | const overlay = document.querySelector("[data-overlay]");
106 |
107 | const togglePlaylist = function () {
108 | playlistSideModal.classList.toggle("active");
109 | overlay.classList.toggle("active");
110 | document.body.classList.toggle("modalActive");
111 | }
112 |
113 | addEventOnElements(playlistTogglers, "click", togglePlaylist);
114 |
115 |
116 |
117 | /**
118 | * PLAYLIST ITEM
119 | *
120 | * remove active state from last time played music
121 | * and add active state in clicked music
122 | */
123 |
124 | const playlistItems = document.querySelectorAll("[data-playlist-item]");
125 |
126 | let currentMusic = 0;
127 | let lastPlayedMusic = 0;
128 |
129 | const changePlaylistItem = function () {
130 | playlistItems[lastPlayedMusic].classList.remove("playing");
131 | playlistItems[currentMusic].classList.add("playing");
132 | }
133 |
134 | addEventOnElements(playlistItems, "click", function () {
135 | lastPlayedMusic = currentMusic;
136 | currentMusic = Number(this.dataset.playlistItem);
137 | changePlaylistItem();
138 | });
139 |
140 |
141 |
142 | /**
143 | * PLAYER
144 | *
145 | * change all visual information on player, based on current music
146 | */
147 |
148 | const playerBanner = document.querySelector("[data-player-banner]");
149 | const playerTitle = document.querySelector("[data-title]");
150 | const playerAlbum = document.querySelector("[data-album]");
151 | const playerYear = document.querySelector("[data-year]");
152 | const playerArtist = document.querySelector("[data-artist]");
153 |
154 | const audioSource = new Audio(musicData[currentMusic].musicPath);
155 |
156 | const changePlayerInfo = function () {
157 | playerBanner.src = musicData[currentMusic].posterUrl;
158 | playerBanner.setAttribute("alt", `${musicData[currentMusic].title} Album Poster`);
159 | document.body.style.backgroundImage = `url(${musicData[currentMusic].backgroundImage})`;
160 | playerTitle.textContent = musicData[currentMusic].title;
161 | playerAlbum.textContent = musicData[currentMusic].album;
162 | playerYear.textContent = musicData[currentMusic].year;
163 | playerArtist.textContent = musicData[currentMusic].artist;
164 |
165 | audioSource.src = musicData[currentMusic].musicPath;
166 |
167 | audioSource.addEventListener("loadeddata", updateDuration);
168 | playMusic();
169 | }
170 |
171 | addEventOnElements(playlistItems, "click", changePlayerInfo);
172 |
173 | /** update player duration */
174 | const playerDuration = document.querySelector("[data-duration]");
175 | const playerSeekRange = document.querySelector("[data-seek]");
176 |
177 | /** pass seconds and get timcode formate */
178 | const getTimecode = function (duration) {
179 | const minutes = Math.floor(duration / 60);
180 | const seconds = Math.ceil(duration - (minutes * 60));
181 | const timecode = `${minutes}:${seconds < 10 ? "0" : ""}${seconds}`;
182 | return timecode;
183 | }
184 |
185 | const updateDuration = function () {
186 | playerSeekRange.max = Math.ceil(audioSource.duration);
187 | playerDuration.textContent = getTimecode(Number(playerSeekRange.max));
188 | }
189 |
190 | audioSource.addEventListener("loadeddata", updateDuration);
191 |
192 |
193 |
194 | /**
195 | * PLAY MUSIC
196 | *
197 | * play and pause music when click on play button
198 | */
199 |
200 | const playBtn = document.querySelector("[data-play-btn]");
201 |
202 | let playInterval;
203 |
204 | const playMusic = function () {
205 | if (audioSource.paused) {
206 | audioSource.play();
207 | playBtn.classList.add("active");
208 | playInterval = setInterval(updateRunningTime, 500);
209 | } else {
210 | audioSource.pause();
211 | playBtn.classList.remove("active");
212 | clearInterval(playInterval);
213 | }
214 | }
215 |
216 | playBtn.addEventListener("click", playMusic);
217 |
218 |
219 | /** update running time while playing music */
220 |
221 | const playerRunningTime = document.querySelector("[data-running-time");
222 |
223 | const updateRunningTime = function () {
224 | playerSeekRange.value = audioSource.currentTime;
225 | playerRunningTime.textContent = getTimecode(audioSource.currentTime);
226 |
227 | updateRangeFill();
228 | isMusicEnd();
229 | }
230 |
231 |
232 |
233 | /**
234 | * RANGE FILL WIDTH
235 | *
236 | * change 'rangeFill' width, while changing range value
237 | */
238 |
239 | const ranges = document.querySelectorAll("[data-range]");
240 | const rangeFill = document.querySelector("[data-range-fill]");
241 |
242 | const updateRangeFill = function () {
243 | let element = this || ranges[0];
244 |
245 | const rangeValue = (element.value / element.max) * 100;
246 | element.nextElementSibling.style.width = `${rangeValue}%`;
247 | }
248 |
249 | addEventOnElements(ranges, "input", updateRangeFill);
250 |
251 |
252 |
253 | /**
254 | * SEEK MUSIC
255 | *
256 | * seek music while changing player seek range
257 | */
258 |
259 | const seek = function () {
260 | audioSource.currentTime = playerSeekRange.value;
261 | playerRunningTime.textContent = getTimecode(playerSeekRange.value);
262 | }
263 |
264 | playerSeekRange.addEventListener("input", seek);
265 |
266 |
267 |
268 | /**
269 | * END MUSIC
270 | */
271 |
272 | const isMusicEnd = function () {
273 | if (audioSource.ended) {
274 | playBtn.classList.remove("active");
275 | audioSource.currentTime = 0;
276 | playerSeekRange.value = audioSource.currentTime;
277 | playerRunningTime.textContent = getTimecode(audioSource.currentTime);
278 | updateRangeFill();
279 | }
280 | }
281 |
282 |
283 |
284 | /**
285 | * SKIP TO NEXT MUSIC
286 | */
287 |
288 | const playerSkipNextBtn = document.querySelector("[data-skip-next]");
289 |
290 | const skipNext = function () {
291 | lastPlayedMusic = currentMusic;
292 |
293 | if (isShuffled) {
294 | shuffleMusic();
295 | } else {
296 | currentMusic >= musicData.length - 1 ? currentMusic = 0 : currentMusic++;
297 | }
298 |
299 | changePlayerInfo();
300 | changePlaylistItem();
301 | }
302 |
303 | playerSkipNextBtn.addEventListener("click", skipNext);
304 |
305 |
306 |
307 | /**
308 | * SKIP TO PREVIOUS MUSIC
309 | */
310 |
311 | const playerSkipPrevBtn = document.querySelector("[data-skip-prev]");
312 |
313 | const skipPrev = function () {
314 | lastPlayedMusic = currentMusic;
315 |
316 | if (isShuffled) {
317 | shuffleMusic();
318 | } else {
319 | currentMusic <= 0 ? currentMusic = musicData.length - 1 : currentMusic--;
320 | }
321 |
322 | changePlayerInfo();
323 | changePlaylistItem();
324 | }
325 |
326 | playerSkipPrevBtn.addEventListener("click", skipPrev);
327 |
328 |
329 |
330 | /**
331 | * SHUFFLE MUSIC
332 | */
333 |
334 | /** get random number for shuffle */
335 | const getRandomMusic = () => Math.floor(Math.random() * musicData.length);
336 |
337 | const shuffleMusic = () => currentMusic = getRandomMusic();
338 |
339 | const playerShuffleBtn = document.querySelector("[data-shuffle]");
340 | let isShuffled = false;
341 |
342 | const shuffle = function () {
343 | playerShuffleBtn.classList.toggle("active");
344 |
345 | isShuffled = isShuffled ? false : true;
346 | }
347 |
348 | playerShuffleBtn.addEventListener("click", shuffle);
349 |
350 |
351 |
352 | /**
353 | * REPEAT MUSIC
354 | */
355 |
356 | const playerRepeatBtn = document.querySelector("[data-repeat]");
357 |
358 | const repeat = function () {
359 | if (!audioSource.loop) {
360 | audioSource.loop = true;
361 | this.classList.add("active");
362 | } else {
363 | audioSource.loop = false;
364 | this.classList.remove("active");
365 | }
366 | }
367 |
368 | playerRepeatBtn.addEventListener("click", repeat);
369 |
370 |
371 |
372 | /**
373 | * MUSIC VOLUME
374 | *
375 | * increase or decrease music volume when change the volume range
376 | */
377 |
378 | const playerVolumeRange = document.querySelector("[data-volume]");
379 | const playerVolumeBtn = document.querySelector("[data-volume-btn]");
380 |
381 | const changeVolume = function () {
382 | audioSource.volume = playerVolumeRange.value;
383 | audioSource.muted = false;
384 |
385 | if (audioSource.volume <= 0.1) {
386 | playerVolumeBtn.children[0].textContent = "volume_mute";
387 | } else if (audioSource.volume <= 0.5) {
388 | playerVolumeBtn.children[0].textContent = "volume_down";
389 | } else {
390 | playerVolumeBtn.children[0].textContent = "volume_up";
391 | }
392 | }
393 |
394 | playerVolumeRange.addEventListener("input", changeVolume);
395 |
396 |
397 | /**
398 | * MUTE MUSIC
399 | */
400 |
401 | const muteVolume = function () {
402 | if (!audioSource.muted) {
403 | audioSource.muted = true;
404 | playerVolumeBtn.children[0].textContent = "volume_off";
405 | } else {
406 | changeVolume();
407 | }
408 | }
409 |
410 | playerVolumeBtn.addEventListener("click", muteVolume);
--------------------------------------------------------------------------------
/assets/music/music-1.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codewithsadee/music-player/1e1ffb4d6fad2cea6ba3e604ccddcc0d68f3dc4f/assets/music/music-1.mp3
--------------------------------------------------------------------------------
/assets/music/music-2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codewithsadee/music-player/1e1ffb4d6fad2cea6ba3e604ccddcc0d68f3dc4f/assets/music/music-2.mp3
--------------------------------------------------------------------------------
/assets/music/music-3.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codewithsadee/music-player/1e1ffb4d6fad2cea6ba3e604ccddcc0d68f3dc4f/assets/music/music-3.mp3
--------------------------------------------------------------------------------
/assets/music/music-4.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codewithsadee/music-player/1e1ffb4d6fad2cea6ba3e604ccddcc0d68f3dc4f/assets/music/music-4.mp3
--------------------------------------------------------------------------------
/assets/music/music-5.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codewithsadee/music-player/1e1ffb4d6fad2cea6ba3e604ccddcc0d68f3dc4f/assets/music/music-5.mp3
--------------------------------------------------------------------------------
/favicon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
12 | Music Player
13 |
14 |
15 |
16 |
19 |
20 |
21 |
24 |
25 |
26 |
27 |
28 |
31 |
33 |
34 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
46 |
47 |
48 |
49 |
50 | graphic_eq
51 |
52 |
Music Player
53 |
54 |
55 |
56 |
57 | queue_music
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
73 |
74 |
75 |
76 |
77 |
78 |
80 |
81 |
82 |
83 |
84 |
Happy Moments (Master)
85 |
86 |
87 | No Spirit
88 |
89 | 2022
90 |
91 |
92 |
No Spirit x Tonion
93 |
94 |
95 |
96 |
97 |
102 |
103 |
104 | 0:00
105 |
106 | 1:00
107 |
108 |
109 |
110 |
111 |
112 | volume_up
113 |
114 |
115 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 | repeat
128 | repeat_one
129 |
130 |
131 |
132 | skip_previous
133 |
134 |
135 |
136 | play_arrow
137 | pause
138 |
139 |
140 |
141 | skip_next
142 |
143 |
144 |
145 | shuffle
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
162 |
163 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
181 |
182 |
183 |
184 |
185 |
--------------------------------------------------------------------------------
/index.txt:
--------------------------------------------------------------------------------
1 | Music Player
2 |
3 | A web music player html template made by codewithsadee
4 |
5 |
6 |
7 | #---------- TOP APP BAR ----------#
8 |
9 | graphic_eq
10 |
11 | Music Player
12 |
13 | queue_music
14 |
15 |
16 |
17 | #---------- PLAYER ----------#
18 |
19 | alt = Happy Moments (Master) Album Poster
20 |
21 | Happy Moments (Master)
22 |
23 | No Spirit
24 | 2022
25 |
26 | No Spirit x Tonion
27 |
28 | 0:00
29 | 1:00
30 |
31 | volume_up
32 |
33 | repeat
34 | repeat_one
35 |
36 | skip_previous
37 |
38 | play_arrow
39 | pause
40 |
41 | skip_next
42 |
43 | shuffle
44 |
45 |
46 |
47 | #---------- PLAYLIST ----------#
48 |
49 | alt = Happy Moments (Master) Album Poster
50 |
51 | equalizer
--------------------------------------------------------------------------------
/readme-images/desktop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codewithsadee/music-player/1e1ffb4d6fad2cea6ba3e604ccddcc0d68f3dc4f/readme-images/desktop.png
--------------------------------------------------------------------------------
/style-guide.md:
--------------------------------------------------------------------------------
1 | # Essential Stuff
2 |
3 | ## Html import links
4 |
5 | Google font
6 |
7 | ``` html
8 |
9 |
10 |
11 | ```
12 |
13 | Material icon
14 |
15 | ``` html
16 |
17 | ```
18 |
19 | ---
20 |
21 | ## Colors
22 |
23 | ``` css
24 | --eerie-black_a95: hsla(204, 9%, 11%, 0.95);
25 | --eerie-black_a50: hsla(204, 9%, 11%, 0.5);
26 | --eerie-black: hsl(204, 9%, 11%);
27 | --gainsboro: hsl(225, 7%, 89%);
28 | --charcoal: hsl(203, 9%, 28%);
29 | --silver-sand: hsl(208, 12%, 78%);
30 | --light-sky-blue: hsl(200, 100%, 73%);
31 | --prussian-blue: hsl(196, 100%, 14%);
32 | --black: hsl(0, 0%, 0%);
33 | --black_a50: hsla(0, 0%, 0%, 0.5);
34 | --black_a20: hsla(0, 0%, 0%, 0.2);
35 | --light-sky-blue_a8: hsla(200, 100%, 73%, 0.08);
36 | --light-sky-blue_a12: hsla(200, 100%, 73%, 0.12);
37 | --silver-sand_a8: hsla(208, 12%, 78%, 0.08);
38 | --silver-sand_a12: hsla(208, 12%, 78%, 0.12);
39 |
40 | --background: var(--eerie-black);
41 | --background-opacity: var(--eerie-black_a95);
42 | --on-background: var(--gainsboro);
43 | --surface-variant: var(--charcoal);
44 | --on-surface-variant: var(--silver-sand);
45 | --on-surface-variant-hover: var(--light-sky-blue_a8);
46 | --on-surface-variant-focus: var(--light-sky-blue_a12);
47 | --primary: var(--light-sky-blue);
48 | --on-primary: var(--prussian-blue);
49 | ```
50 |
51 | ## Gradient color
52 |
53 | ``` css
54 | --gradient: linear-gradient(180deg, hsla(204, 9%, 11%, 0.90) 60%, transparent 120%);
55 | ```
56 |
57 | ## Typography
58 |
59 | ``` css
60 | --ff-inter: 'Inter', sans-serif;
61 |
62 | --headline-sm: 2.4rem;
63 | --title-lg: 2.2rem;
64 | --label-lg: 1.4rem;
65 | --label-md: 1.2rem;
66 |
67 | --fw-400: 400;
68 | --fw-500: 500;
69 | ```
70 |
71 | ## Shadow
72 |
73 | ``` css
74 | --shadow-1: 0 1px 4px 1px var(--black_a20);
75 | --shadow-2: 0 1px 4px 1px var(--black_a50);
76 | ```
77 |
78 | ## Border Radius
79 |
80 | ``` css
81 | --radius-24: 24px;
82 | --radius-16: 16px;
83 | --radius-12: 12px;
84 | --radius-pill: 100px;
85 | --radius-circle: 50%;
86 | ```
87 |
88 | ## Transition
89 |
90 | ``` css
91 | --transition-1: 200ms cubic-bezier(0.2, 0, 0, 1);
92 | --transition-2: 300ms cubic-bezier(0.2, 0, 0, 1);
93 | ```
94 |
--------------------------------------------------------------------------------