commit 802a85e000170be6c84bef6b5197b38a5578f534 Author: Sadee Date: Wed Nov 9 19:50:22 2022 +0600 initial diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d8763cc --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Sadee + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..bed02f1 --- /dev/null +++ b/README.md @@ -0,0 +1,54 @@ +
+ + ![GitHub repo size](https://img.shields.io/github/repo-size/codewithsadee/music-player) + ![GitHub stars](https://img.shields.io/github/stars/codewithsadee/music-player?style=social) + ![GitHub forks](https://img.shields.io/github/forks/codewithsadee/music-player?style=social) + [![Twitter Follow](https://img.shields.io/twitter/follow/codewithsadee?style=social)](https://twitter.com/intent/follow?screen_name=codewithsadee) + [![YouTube Video Views](https://img.shields.io/youtube/views/jbMd2NVFrZk?style=social)](https://youtu.be/jbMd2NVFrZk) + +
+
+ +

Web Music Player

+ + A fully responsive web music player using vanilla javascript,
Responsive for all devices, build using html, css, and javascript. + + ➥ Live Demo + +
+ +
+ +### Demo Screeshots + +![Music Player Desktop Demo](./readme-images/desktop.png "Desktop Demo") + +### Prerequisites + +Before you begin, ensure you have met the following requirements: + +* [Git](https://git-scm.com/downloads "Download Git") must be installed on your operating system. + +### Run Locally + +To run **Music Player** locally, run this command on your git bash: + +Linux and macOS: + +```bash +sudo git clone https://github.com/codewithsadee/music-player.git +``` + +Windows: + +```bash +git clone https://github.com/codewithsadee/music-player.git +``` + +### Contact + +If you want to contact with me you can reach me at [Twitter](https://www.twitter.com/codewithsadee). + +### License + +This project is **free to use** and does not contains any license. diff --git a/assets/css/style.css b/assets/css/style.css new file mode 100644 index 0000000..13b4cbe --- /dev/null +++ b/assets/css/style.css @@ -0,0 +1,683 @@ +/*-----------------------------------*\ + #style.css +\*-----------------------------------*/ + +/** + * copyright 2022 codewithsadee + */ + + + + + +/*-----------------------------------*\ + #CUSTOM PROPERTY +\*-----------------------------------*/ + +:root { + + /** + * colors + */ + + --eerie-black_a95: hsla(204, 9%, 11%, 0.95); + --eerie-black_a50: hsla(204, 9%, 11%, 0.5); + --eerie-black: hsl(204, 9%, 11%); + --gainsboro: hsl(225, 7%, 89%); + --charcoal: hsl(203, 9%, 28%); + --silver-sand: hsl(208, 12%, 78%); + --light-sky-blue: hsl(200, 100%, 73%); + --prussian-blue: hsl(196, 100%, 14%); + --black: hsl(0, 0%, 0%); + --black_a50: hsla(0, 0%, 0%, 0.5); + --black_a20: hsla(0, 0%, 0%, 0.2); + --light-sky-blue_a8: hsla(200, 100%, 73%, 0.08); + --light-sky-blue_a12: hsla(200, 100%, 73%, 0.12); + --silver-sand_a8: hsla(208, 12%, 78%, 0.08); + --silver-sand_a12: hsla(208, 12%, 78%, 0.12); + + --background: var(--eerie-black); + --background-opacity: var(--eerie-black_a95); + --on-background: var(--gainsboro); + --surface-variant: var(--charcoal); + --on-surface-variant: var(--silver-sand); + --on-surface-variant-hover: var(--light-sky-blue_a8); + --on-surface-variant-focus: var(--light-sky-blue_a12); + --primary: var(--light-sky-blue); + --on-primary: var(--prussian-blue); + + /** + * gradient + */ + + --gradient: linear-gradient(180deg, hsla(204, 9%, 11%, 0.90) 60%, transparent 120%); + + /** + * typography + */ + + --ff-inter: 'Inter', sans-serif; + + --headline-sm: 2.4rem; + --title-lg: 2.2rem; + --label-lg: 1.4rem; + --label-md: 1.2rem; + + --fw-400: 400; + --fw-500: 500; + + /** + * box shadow + */ + + --shadow-1: 0 1px 4px 1px var(--black_a20); + --shadow-2: 0 1px 4px 1px var(--black_a50); + + /** + * border radius + */ + + --radius-24: 24px; + --radius-16: 16px; + --radius-12: 12px; + --radius-pill: 100px; + --radius-circle: 50%; + + /** + * transition + */ + + --transition-1: 200ms cubic-bezier(0.2, 0, 0, 1); + --transition-2: 300ms cubic-bezier(0.2, 0, 0, 1); + +} + + + + + +/*-----------------------------------*\ + #RESET +\*-----------------------------------*/ + +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +li { list-style: none; } + +a, +img, +span, +input, +button { display: block; } + +a { + text-decoration: none; + color: inherit; +} + +img { height: auto; } + +input, +button { + background: none; + border: none; + font: inherit; +} + +input { width: 100%; } + +button { cursor: pointer; } + +html { + font-family: var(--ff-inter); + font-size: 10px; + scroll-behavior: smooth; +} + +body { + background-color: var(--black); + color: var(--on-background); + font-size: 1.6rem; + line-height: 1.5; + min-height: 100vh; + min-width: 250px; + background-image: url("../images/poster-1.jpg"); + background-size: 150%; + background-repeat: no-repeat; + background-position: top; + overflow: overlay; +} + +body.modalActive { overflow: hidden; } + +::-webkit-scrollbar { width: 8px; } + +::-webkit-scrollbar-track { background: transparent; } + +::-webkit-scrollbar-thumb { + background-color: transparent; + border-radius: var(--radius-pill); +} + +body:hover::-webkit-scrollbar-thumb { background-color: var(--surface-variant); } + + + + + +/*-----------------------------------*\ + #REUSED STYLE +\*-----------------------------------*/ + +.material-symbols-rounded { + font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' -25, 'opsz' 24; + width: 1em; + height: 1em; + overflow: hidden; + user-select: none; +} + +.wrapper { + display: flex; + align-items: center; +} + +.title-lg { + font-size: var(--title-lg); + font-weight: var(--fw-400); +} + +.btn-icon { + color: var(--on-surface-variant); + width: 40px; + height: 40px; + display: grid; + place-items: center; + border-radius: var(--radius-circle); +} + +.btn-icon:hover { background-color: var(--on-surface-variant-hover); } + +.btn-icon:is(:focus, :focus-visible) { + background-color: var(--on-surface-variant-focus); +} + +.img-cover { + width: 100%; + height: 100%; + object-fit: cover; +} + +.headline-sm { + font-size: var(--headline-sm); + font-weight: var(--fw-400); +} + +.label-lg, +.label-md { font-weight: var(--fw-500); } + +.label-lg { + font-size: var(--label-lg); + letter-spacing: 0.1px; +} + +.label-md { + font-size: var(--label-md); + letter-spacing: 0.5px; +} + + + + + +/*-----------------------------------*\ + #TOP APP BAR +\*-----------------------------------*/ + +.top-bar { + position: fixed; + top: 0; + left: 0; + background-color: var(--background-opacity); + min-width: 250px; + width: 100%; + height: 64px; + padding-inline: 16px; + justify-content: space-between; + backdrop-filter: blur(50px); + box-shadow: var(--shadow-1); + z-index: 2; +} + +.logo { gap: 12px; } + + + + + +/*-----------------------------------*\ + #PLAYER +\*-----------------------------------*/ + +.volume { display: none; } + +.player { + --padding: 24px; + background-image: var(--gradient); + padding: var(--padding); + padding-block-start: calc(64px + var(--padding)); + min-height: 100vh; + display: flex; + text-align: center; + backdrop-filter: blur(30px); + overflow: hidden; +} + +.player .container { + width: 100%; + display: grid; + grid-template-rows: 1fr max-content; + gap: 24px; +} + +.music-banner { + aspect-ratio: 1 / 1; + background-color: var(--surface-variant); + max-width: 400px; + margin-inline: auto; + align-self: center; + overflow: hidden; + border-radius: var(--radius-24); +} + +.label-wrapper { + justify-content: center; + opacity: 0.8; + margin-block: 8px 4px; +} + +.label-wrapper span:first-child::after { + content: "|"; + display: inline-block; + margin-inline: 4px; +} + +.artist { + opacity: 0.6; + margin-block-end: 36px; +} + +.seek-control { margin-block-end: 20px; } + +.range-wrapper { position: relative; } + +.range { + appearance: none; + cursor: pointer; +} + +.range::-webkit-slider-runnable-track { + appearance: none; + background-color: var(--surface-variant); + height: 6px; + width: 100%; + border-radius: var(--radius-pill); +} + +.range-fill { + content: ""; + position: absolute; + top: 0; + left: 0; + width: 0; + height: 6px; + background-color: var(--primary); + border-radius: var(--radius-pill); + pointer-events: none; +} + +.range::-webkit-slider-thumb { + appearance: none; + background-color: var(--primary); + width: 16px; + height: 16px; + margin-block-start: -5px; + border-radius: var(--radius-pill); + transition: var(--transition-1); +} + +.range::-webkit-slider-thumb:hover { + box-shadow: 0 0 0 12px var(--on-surface-variant-hover); +} + +.range::-webkit-slider-thumb:is(:focus, :focus-visible) { + box-shadow: 0 0 0 12px var(--on-surface-variant-focus); +} + +.duration-label { + justify-content: space-between; + margin-block-start: 12px; +} + +.player-control { justify-content: space-evenly; } + +.player-control .toggle.active { color: var(--primary); } + +.player-control .play { + background-color: var(--surface-variant); + color: var(--primary); +} + +.player-control .play.active { + background-color: var(--primary); + color: var(--on-primary); +} + +.player-control .btn-icon.active .default-icon, +.player-control .btn-icon .active-icon { display: none; } + +.player-control .btn-icon .default-icon, +.player-control .btn-icon.active .active-icon { display: block; } + + + + + +/*-----------------------------------*\ + #PLAYLIST +\*-----------------------------------*/ + +.playlist { + position: fixed; + top: 0; + right: -180px; + width: 180px; + height: 100vh; + background-color: var(--background); + padding: 28px; + border-top-left-radius: var(--radius-16); + border-bottom-left-radius: var(--radius-16); + box-shadow: var(--shadow-2); + overflow-y: overlay; + visibility: hidden; + transition: var(--transition-2); + z-index: 4; +} + +.playlist:hover::-webkit-scrollbar-thumb { background-color: var(--surface-variant); } + +.playlist.active { + transform: translateX(-180px); + visibility: visible; +} + +.music-list { + display: grid; + gap: 20px; +} + +.music-item { + position: relative; + border-radius: var(--radius-12); + background-color: var(--surface-variant); + overflow: hidden; +} + +.music-item .item-icon { + position: absolute; + inset: 0; + background-color: var(--eerie-black_a50); + display: grid; + place-items: center; + opacity: 0; +} + +.music-item.playing .item-icon { opacity: 1; } + +.music-item .item-icon .material-symbols-rounded { + color: var(--primary); + font-size: 3rem; +} + +.music-item:is(:hover, :focus-visible, :active, .playing) { + outline: 1px solid var(--primary); +} + +.overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100vh; + background-color: var(--black); + opacity: 0; + pointer-events: none; + transition: var(--transition-2); + z-index: 3; +} + +.overlay.active { + pointer-events: all; + opacity: 0.5; +} + + + + + +/*-----------------------------------*\ + #MEDIA QUERIES +\*-----------------------------------*/ + +/** + * responsive for large than 575px screen + */ + +@media (min-width: 575px) { + + /** + * RESET + */ + + body { background-size: 100%; } + + + + /** + * PLAYER + */ + + .player { justify-content: center; } + + .player .container { max-width: 540px; } + +} + + + + + +/** + * responsive for large than 992px screen + */ + +@media (min-width: 992px) { + + /** + * CUSTOM PROPERTY + */ + + :root { + + /** + * typography + */ + + --headline-sm: 4.2rem; + --label-lg: 2.2rem; + + } + + + + /** + * RESET + */ + + body { + background-size: 40% 100%; + background-position: left center; + } + + article { display: flex; } + + + + /** + * REUSED STYLE + */ + + .btn-icon { + width: 54px; + height: 54px; + } + + .btn-icon .material-symbols-rounded { font-size: 2.8rem; } + + + + /** + * TOP APP BAR + */ + + .top-bar-actions { display: none; } + + + + /** + * PLAYER + */ + + .player { + --padding: 48px; + text-align: left; + flex-grow: 1; + align-items: center; + backdrop-filter: blur(100px); + } + + .player .container { + max-width: 1200px; + grid-template-columns: 0.8fr 1fr; + grid-template-rows: 1fr; + gap: 48px; + max-height: 500px; + height: 100%; + } + + .music-banner { + aspect-ratio: unset; + max-width: max-content; + width: 100%; + height: 100%; + } + + .music-content { + display: flex; + flex-direction: column; + padding-block-start: 48px; + min-width: 100%; + } + + .music-content :is(.headline-sm, .label-lg, .label-md) { + max-width: 85%; + } + + .label-wrapper { + justify-content: flex-start; + margin-block-end: 8px; + } + + .artist { --label-md: 1.8rem; } + + .seek-control { + margin-block-start: auto; + display: grid; + grid-template-columns: 1fr 150px; + align-items: center; + gap: 24px; + } + + .volume { + display: flex; + align-items: center; + gap: 4px; + margin-block-start: -30px; + } + + .volume .btn-icon { flex-shrink: 0; } + + .volume .range-fill { width: 100%; } + + .player-control { + margin-inline-end: 174px; + min-width: max-content; + gap: 8px; + } + + + + /** + * PLAYLIST + */ + + .overlay { display: none; } + + .playlist { + position: static; + visibility: visible; + border-radius: 0; + box-shadow: none; + flex-shrink: 0; + } + + .playlist.active { transform: unset; } + + .music-item:is(:hover, :focus-visible, :active, .playing) { + outline: 2px solid var(--primary); + } + + .music-item .item-icon .material-symbols-rounded { + font-size: 3.6rem; + } + +} + + + + + +/** + * responsive for large than 1200px screen + */ + +@media (min-width: 1200px) { + + /** + * PLAYLIST + */ + + .playlist { + padding: 32px; + width: 220px; + } + + .music-list { gap: 28px; } + +} \ No newline at end of file diff --git a/assets/images/Thumbs.db b/assets/images/Thumbs.db new file mode 100644 index 0000000..e01db6a Binary files /dev/null and b/assets/images/Thumbs.db differ diff --git a/assets/images/poster-1.jpg b/assets/images/poster-1.jpg new file mode 100644 index 0000000..63d4fa6 Binary files /dev/null and b/assets/images/poster-1.jpg differ diff --git a/assets/images/poster-2.jpg b/assets/images/poster-2.jpg new file mode 100644 index 0000000..8a5a8c0 Binary files /dev/null and b/assets/images/poster-2.jpg differ diff --git a/assets/images/poster-3.jpg b/assets/images/poster-3.jpg new file mode 100644 index 0000000..0c3b9c6 Binary files /dev/null and b/assets/images/poster-3.jpg differ diff --git a/assets/images/poster-4.jpg b/assets/images/poster-4.jpg new file mode 100644 index 0000000..69643d3 Binary files /dev/null and b/assets/images/poster-4.jpg differ diff --git a/assets/images/poster-5.jpg b/assets/images/poster-5.jpg new file mode 100644 index 0000000..fa28f49 Binary files /dev/null and b/assets/images/poster-5.jpg differ diff --git a/assets/js/script.js b/assets/js/script.js new file mode 100644 index 0000000..476e906 --- /dev/null +++ b/assets/js/script.js @@ -0,0 +1,410 @@ +'use strict'; + + + +/** + * all music information + */ + +const musicData = [ + { + backgroundImage: "./assets/images/poster-1.jpg", + posterUrl: "./assets/images/poster-1.jpg", + title: "Happy Moments (Master)", + album: "No Spirit", + year: 2022, + artist: "No Spirit x Tonion", + musicPath: "./assets/music/music-1.mp3", + }, + { + backgroundImage: "./assets/images/poster-2.jpg", + posterUrl: "./assets/images/poster-2.jpg", + title: "We Are Going To Be Ok (Master)", + album: "No Spirit", + year: 2022, + artist: "No Spirit x Jhove", + musicPath: "./assets/music/music-2.mp3", + }, + { + backgroundImage: "./assets/images/poster-3.jpg", + posterUrl: "./assets/images/poster-3.jpg", + title: "Winter Meadow", + album: "No Spirit", + year: 2022, + artist: "No Spirit x juniorodeo", + musicPath: "./assets/music/music-3.mp3", + }, + { + backgroundImage: "./assets/images/poster-4.jpg", + posterUrl: "./assets/images/poster-4.jpg", + title: "From Where We Started", + album: "No Spirit", + year: 2022, + artist: "No Spirit", + musicPath: "./assets/music/music-4.mp3", + }, + { + backgroundImage: "./assets/images/poster-5.jpg", + posterUrl: "./assets/images/poster-5.jpg", + title: "Where I Found You", + album: "No Spirit", + year: 2022, + artist: "No Spirit", + musicPath: "./assets/music/music-5.mp3", + }, +]; + + + +/** + * add eventListnere on all elements that are passed + */ + +const addEventOnElements = function (elements, eventType, callback) { + for (let i = 0, len = elements.length; i < len; i++) { + elements[i].addEventListener(eventType, callback); + } +} + + + +/** + * PLAYLIST + * + * add all music in playlist, from 'musicData' + */ + +const playlist = document.querySelector("[data-music-list]"); + +for (let i = 0, len = musicData.length; i < len; i++) { + playlist.innerHTML += ` +
  • + +
  • + `; +} + + + +/** + * PLAYLIST MODAL SIDEBAR TOGGLE + * + * show 'playlist' modal sidebar when click on playlist button in top app bar + * and hide when click on overlay or any playlist-item + */ + +const playlistSideModal = document.querySelector("[data-playlist]"); +const playlistTogglers = document.querySelectorAll("[data-playlist-toggler]"); +const overlay = document.querySelector("[data-overlay]"); + +const togglePlaylist = function () { + playlistSideModal.classList.toggle("active"); + overlay.classList.toggle("active"); + document.body.classList.toggle("modalActive"); +} + +addEventOnElements(playlistTogglers, "click", togglePlaylist); + + + +/** + * PLAYLIST ITEM + * + * remove active state from last time played music + * and add active state in clicked music + */ + +const playlistItems = document.querySelectorAll("[data-playlist-item]"); + +let currentMusic = 0; +let lastPlayedMusic = 0; + +const changePlaylistItem = function () { + playlistItems[lastPlayedMusic].classList.remove("playing"); + playlistItems[currentMusic].classList.add("playing"); +} + +addEventOnElements(playlistItems, "click", function () { + lastPlayedMusic = currentMusic; + currentMusic = Number(this.dataset.playlistItem); + changePlaylistItem(); +}); + + + +/** + * PLAYER + * + * change all visual information on player, based on current music + */ + +const playerBanner = document.querySelector("[data-player-banner]"); +const playerTitle = document.querySelector("[data-title]"); +const playerAlbum = document.querySelector("[data-album]"); +const playerYear = document.querySelector("[data-year]"); +const playerArtist = document.querySelector("[data-artist]"); + +const audioSource = new Audio(musicData[currentMusic].musicPath); + +const changePlayerInfo = function () { + playerBanner.src = musicData[currentMusic].posterUrl; + playerBanner.setAttribute("alt", `${musicData[currentMusic].title} Album Poster`); + document.body.style.backgroundImage = `url(${musicData[currentMusic].backgroundImage})`; + playerTitle.textContent = musicData[currentMusic].title; + playerAlbum.textContent = musicData[currentMusic].album; + playerYear.textContent = musicData[currentMusic].year; + playerArtist.textContent = musicData[currentMusic].artist; + + audioSource.src = musicData[currentMusic].musicPath; + + audioSource.addEventListener("loadeddata", updateDuration); + playMusic(); +} + +addEventOnElements(playlistItems, "click", changePlayerInfo); + +/** update player duration */ +const playerDuration = document.querySelector("[data-duration]"); +const playerSeekRange = document.querySelector("[data-seek]"); + +/** pass seconds and get timcode formate */ +const getTimecode = function (duration) { + const minutes = Math.floor(duration / 60); + const seconds = Math.ceil(duration - (minutes * 60)); + const timecode = `${minutes}:${seconds < 10 ? "0" : ""}${seconds}`; + return timecode; +} + +const updateDuration = function () { + playerSeekRange.max = Math.ceil(audioSource.duration); + playerDuration.textContent = getTimecode(Number(playerSeekRange.max)); +} + +audioSource.addEventListener("loadeddata", updateDuration); + + + +/** + * PLAY MUSIC + * + * play and pause music when click on play button + */ + +const playBtn = document.querySelector("[data-play-btn]"); + +let playInterval; + +const playMusic = function () { + if (audioSource.paused) { + audioSource.play(); + playBtn.classList.add("active"); + playInterval = setInterval(updateRunningTime, 500); + } else { + audioSource.pause(); + playBtn.classList.remove("active"); + clearInterval(playInterval); + } +} + +playBtn.addEventListener("click", playMusic); + + +/** update running time while playing music */ + +const playerRunningTime = document.querySelector("[data-running-time"); + +const updateRunningTime = function () { + playerSeekRange.value = audioSource.currentTime; + playerRunningTime.textContent = getTimecode(audioSource.currentTime); + + updateRangeFill(); + isMusicEnd(); +} + + + +/** + * RANGE FILL WIDTH + * + * change 'rangeFill' width, while changing range value + */ + +const ranges = document.querySelectorAll("[data-range]"); +const rangeFill = document.querySelector("[data-range-fill]"); + +const updateRangeFill = function () { + let element = this || ranges[0]; + + const rangeValue = (element.value / element.max) * 100; + element.nextElementSibling.style.width = `${rangeValue}%`; +} + +addEventOnElements(ranges, "input", updateRangeFill); + + + +/** + * SEEK MUSIC + * + * seek music while changing player seek range + */ + +const seek = function () { + audioSource.currentTime = playerSeekRange.value; + playerRunningTime.textContent = getTimecode(playerSeekRange.value); +} + +playerSeekRange.addEventListener("input", seek); + + + +/** + * END MUSIC + */ + +const isMusicEnd = function () { + if (audioSource.ended) { + playBtn.classList.remove("active"); + audioSource.currentTime = 0; + playerSeekRange.value = audioSource.currentTime; + playerRunningTime.textContent = getTimecode(audioSource.currentTime); + updateRangeFill(); + } +} + + + +/** + * SKIP TO NEXT MUSIC + */ + +const playerSkipNextBtn = document.querySelector("[data-skip-next]"); + +const skipNext = function () { + lastPlayedMusic = currentMusic; + + if (isShuffled) { + shuffleMusic(); + } else { + currentMusic >= musicData.length - 1 ? currentMusic = 0 : currentMusic++; + } + + changePlayerInfo(); + changePlaylistItem(); +} + +playerSkipNextBtn.addEventListener("click", skipNext); + + + +/** + * SKIP TO PREVIOUS MUSIC + */ + +const playerSkipPrevBtn = document.querySelector("[data-skip-prev]"); + +const skipPrev = function () { + lastPlayedMusic = currentMusic; + + if (isShuffled) { + shuffleMusic(); + } else { + currentMusic <= 0 ? currentMusic = musicData.length - 1 : currentMusic--; + } + + changePlayerInfo(); + changePlaylistItem(); +} + +playerSkipPrevBtn.addEventListener("click", skipPrev); + + + +/** + * SHUFFLE MUSIC + */ + +/** get random number for shuffle */ +const getRandomMusic = () => Math.floor(Math.random() * musicData.length); + +const shuffleMusic = () => currentMusic = getRandomMusic(); + +const playerShuffleBtn = document.querySelector("[data-shuffle]"); +let isShuffled = false; + +const shuffle = function () { + playerShuffleBtn.classList.toggle("active"); + + isShuffled = isShuffled ? false : true; +} + +playerShuffleBtn.addEventListener("click", shuffle); + + + +/** + * REPEAT MUSIC + */ + +const playerRepeatBtn = document.querySelector("[data-repeat]"); + +const repeat = function () { + if (!audioSource.loop) { + audioSource.loop = true; + this.classList.add("active"); + } else { + audioSource.loop = false; + this.classList.remove("active"); + } +} + +playerRepeatBtn.addEventListener("click", repeat); + + + +/** + * MUSIC VOLUME + * + * increase or decrease music volume when change the volume range + */ + +const playerVolumeRange = document.querySelector("[data-volume]"); +const playerVolumeBtn = document.querySelector("[data-volume-btn]"); + +const changeVolume = function () { + audioSource.volume = playerVolumeRange.value; + audioSource.muted = false; + + if (audioSource.volume <= 0.1) { + playerVolumeBtn.children[0].textContent = "volume_mute"; + } else if (audioSource.volume <= 0.5) { + playerVolumeBtn.children[0].textContent = "volume_down"; + } else { + playerVolumeBtn.children[0].textContent = "volume_up"; + } +} + +playerVolumeRange.addEventListener("input", changeVolume); + + +/** + * MUTE MUSIC + */ + +const muteVolume = function () { + if (!audioSource.muted) { + audioSource.muted = true; + playerVolumeBtn.children[0].textContent = "volume_off"; + } else { + changeVolume(); + } +} + +playerVolumeBtn.addEventListener("click", muteVolume); \ No newline at end of file diff --git a/assets/music/music-1.mp3 b/assets/music/music-1.mp3 new file mode 100644 index 0000000..951c876 Binary files /dev/null and b/assets/music/music-1.mp3 differ diff --git a/assets/music/music-2.mp3 b/assets/music/music-2.mp3 new file mode 100644 index 0000000..c157db9 Binary files /dev/null and b/assets/music/music-2.mp3 differ diff --git a/assets/music/music-3.mp3 b/assets/music/music-3.mp3 new file mode 100644 index 0000000..ff45a28 Binary files /dev/null and b/assets/music/music-3.mp3 differ diff --git a/assets/music/music-4.mp3 b/assets/music/music-4.mp3 new file mode 100644 index 0000000..c3e3fdf Binary files /dev/null and b/assets/music/music-4.mp3 differ diff --git a/assets/music/music-5.mp3 b/assets/music/music-5.mp3 new file mode 100644 index 0000000..7aa3e15 Binary files /dev/null and b/assets/music/music-5.mp3 differ diff --git a/favicon.svg b/favicon.svg new file mode 100644 index 0000000..01cd8ef --- /dev/null +++ b/favicon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..56180ff --- /dev/null +++ b/index.html @@ -0,0 +1,185 @@ + + + + + + + + + + Music Player + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + +
    + +
    + + + + + +
    +
    + + + +
    +
    + +
    + Happy Moments (Master) Album Poster +
    + +
    + +

    Happy Moments (Master)

    + +

    + No Spirit + + 2022 +

    + +

    No Spirit x Tonion

    + +
    + +
    +
    + + +
    +
    + +
    + 0:00 + + 1:00 +
    +
    + +
    + + +
    + + +
    +
    +
    + +
    + +
    + + + + + + + + + + + +
    + +
    + +
    +
    + + + + + + + +
    + +
      + +
      + +
      + +
      +
      + + + + + + + + + + + \ No newline at end of file diff --git a/index.txt b/index.txt new file mode 100644 index 0000000..ff010be --- /dev/null +++ b/index.txt @@ -0,0 +1,51 @@ +Music Player + +A web music player html template made by codewithsadee + + + +#---------- TOP APP BAR ----------# + +graphic_eq + +Music Player + +queue_music + + + +#---------- PLAYER ----------# + +alt = Happy Moments (Master) Album Poster + +Happy Moments (Master) + +No Spirit +2022 + +No Spirit x Tonion + +0:00 +1:00 + +volume_up + +repeat +repeat_one + +skip_previous + +play_arrow +pause + +skip_next + +shuffle + + + +#---------- PLAYLIST ----------# + +alt = Happy Moments (Master) Album Poster + +equalizer \ No newline at end of file diff --git a/readme-images/desktop.png b/readme-images/desktop.png new file mode 100644 index 0000000..62f9d6c Binary files /dev/null and b/readme-images/desktop.png differ diff --git a/style-guide.md b/style-guide.md new file mode 100644 index 0000000..b5de542 --- /dev/null +++ b/style-guide.md @@ -0,0 +1,93 @@ +# Essential Stuff + +## Html import links + +Google font + +``` html + + + +``` + +Material icon + +``` html + +``` + +--- + +## Colors + +``` css +--eerie-black_a95: hsla(204, 9%, 11%, 0.95); +--eerie-black_a50: hsla(204, 9%, 11%, 0.5); +--eerie-black: hsl(204, 9%, 11%); +--gainsboro: hsl(225, 7%, 89%); +--charcoal: hsl(203, 9%, 28%); +--silver-sand: hsl(208, 12%, 78%); +--light-sky-blue: hsl(200, 100%, 73%); +--prussian-blue: hsl(196, 100%, 14%); +--black: hsl(0, 0%, 0%); +--black_a50: hsla(0, 0%, 0%, 0.5); +--black_a20: hsla(0, 0%, 0%, 0.2); +--light-sky-blue_a8: hsla(200, 100%, 73%, 0.08); +--light-sky-blue_a12: hsla(200, 100%, 73%, 0.12); +--silver-sand_a8: hsla(208, 12%, 78%, 0.08); +--silver-sand_a12: hsla(208, 12%, 78%, 0.12); + +--background: var(--eerie-black); +--background-opacity: var(--eerie-black_a95); +--on-background: var(--gainsboro); +--surface-variant: var(--charcoal); +--on-surface-variant: var(--silver-sand); +--on-surface-variant-hover: var(--light-sky-blue_a8); +--on-surface-variant-focus: var(--light-sky-blue_a12); +--primary: var(--light-sky-blue); +--on-primary: var(--prussian-blue); +``` + +## Gradient color + +``` css +--gradient: linear-gradient(180deg, hsla(204, 9%, 11%, 0.90) 60%, transparent 120%); +``` + +## Typography + +``` css +--ff-inter: 'Inter', sans-serif; + +--headline-sm: 2.4rem; +--title-lg: 2.2rem; +--label-lg: 1.4rem; +--label-md: 1.2rem; + +--fw-400: 400; +--fw-500: 500; +``` + +## Shadow + +``` css +--shadow-1: 0 1px 4px 1px var(--black_a20); +--shadow-2: 0 1px 4px 1px var(--black_a50); +``` + +## Border Radius + +``` css +--radius-24: 24px; +--radius-16: 16px; +--radius-12: 12px; +--radius-pill: 100px; +--radius-circle: 50%; +``` + +## Transition + +``` css +--transition-1: 200ms cubic-bezier(0.2, 0, 0, 1); +--transition-2: 300ms cubic-bezier(0.2, 0, 0, 1); +```