<div id="root"></div>
body {
background-color: #e6f4f9;
display: flex;
justify-content: center;
align-items: center;
margin: 50px 0;
width: 100%;
font-family: "Bitter", serif;
}
.container {
height: max-content;
width: 400px;
border-radius: 30px;
background-color: #63Add0;
box-shadow: 2px 5px 5px #cccccc, -1px 7px 5px #cccccc;
}
.header{
display:flex;
justify-content: space-between;
padding: 20px 15px 5px 15px;
}
.header button{
cursor: pointer;
}
.icon{
background: transparent;
border: none;
font-size: 20px;
color: white;
}
.headerText{
color: white;
font-size: 20px;
}
.playerContaier{
width: 100%;
min-height: 200px;
background-color: white;
border-radius: 35px 35px 0 0;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
padding: 75px 0 30px 0;
margin-top: 130px;
border-radius: 30px;
}
.avatar, .avatar1{
position: absolute;
top:130px;
left: auto;
right: auto;
width: 200px;
height:200px;
border-radius: 30px;
}
.name{
margin-top: 27px;
margin-bottom: 10px;
width:100%;
text-align:center;
}
.title{
margin-top: 0;
margin-bottom: 5px;
width:100%;
text-align:center;
}
.avatar1{
top: 140px;
width: 190px;
filter: blur(8px);
}
.controls{
width: 60%;
display:flex;
justify-content:space-between;
align-items: center;
margin: 0;
}
.controlButton{
border: none;
background-color: transparent;
cursor: pointer;
border-radius: 50%;
width: 60px;
height: 60px;
display: flex;
justify-content: center;
align-items: center;
font-size: 25px;
transition: box-shadow 0.4s;
}
.controlButton:hover{
box-shadow: 3px 3px 5px #cccccc, -3px 0px 5px #cccccc;
}
.centerButton{
border: none;
background-color: transparent;
cursor: pointer;
border-radius: 50%;
width: 80px;
height: 80px;
display: flex;
justify-content: center;
align-items: center;
font-size: 40px;
border: 7px solid #63ADD0;
color: #63ADD0;
transition: box-shadow 0.5s;
}
.progress{
margin: 5px 0;
width: 100%;
display: flex;
justify-content: space-evenly;
align-items: center;
}
.progressCenter{
width: 70%;
height: 7px;
background-color: #d9d9d9;
border-radius: 10px;
}
.progressCenter:hover{
cursor: pointer;
}
.progressBar{
width: 0px;
height: 7px;
border-radius: 10px;
background-color: #63ADD0;
}
.options{
display: flex;
align-items:center;
justify-content: space-evenly;
width: 100%;
margin-top: 50px;
}
.opt{
border: none;
background-color: transparent;
font-size: 25px;
height:auto;
width:auto;
border-radius:50%;
padding: 20px;
display: flex;
justify-content: center;
align-items: center;
transition: box-shadow 0.4s;
}
.opt:hover{
cursor:pointer;
box-shadow: 3px 3px 5px #cccccc, -3px 0px 5px #cccccc;
color: #147CC0;
}
/*
Design Credits : https://dribbble.com/shots/6437042-Music-App-UI
Also I couldn't find any open music API
so credits to
https://codepen.io/JavaScriptJunkie/details/qBWrRyg
to make his repo public and make the songs easily accesible
also I used his UI as inspiration too
*/
import * as React from "https://cdn.skypack.dev/react";
import * as ReactDOM from "https://cdn.skypack.dev/react-dom@17.0.1";
import {Transition} from "https://cdn.skypack.dev/react-transition-group@4.4.1";
import {
ThreeDots,
ChevronLeft,
PauseFill,
PlayFill,
SkipBackwardFill,
SkipForwardFill,
Shuffle,
ArrowRepeat,
Heart,
BoxArrowUpRight,
HeartFill
} from "https://cdn.skypack.dev/react-bootstrap-icons@1.5.0";
const tracks = [
{
name: "Mekanın Sahibi",
artist: "Norm Ender",
cover: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/img/1.jpg",
source: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/mp3/1.mp3",
url: "https://www.youtube.com/watch?v=z3wAjJXbYzA",
favorited: false
},
{
name: "Everybody Knows",
artist: "Leonard Cohen",
cover: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/img/2.jpg",
source: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/mp3/2.mp3",
url: "https://www.youtube.com/watch?v=Lin-a2lTelg",
favorited: true
},
{
name: "Extreme Ways",
artist: "Moby",
cover: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/img/3.jpg",
source: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/mp3/3.mp3",
url: "https://www.youtube.com/watch?v=ICjyAe9S54c",
favorited: false
},
{
name: "Butterflies",
artist: "Sia",
cover: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/img/4.jpg",
source: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/mp3/4.mp3",
url: "https://www.youtube.com/watch?v=kYgGwWYOd9Y",
favorited: false
},
{
name: "The Final Victory",
artist: "Haggard",
cover: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/img/5.jpg",
source: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/mp3/5.mp3",
url: "https://www.youtube.com/watch?v=0WlpALnQdN8",
favorited: true
},
{
name: "Genius ft. Sia, Diplo, Labrinth",
artist: "LSD",
cover: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/img/6.jpg",
source: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/mp3/6.mp3",
url: "https://www.youtube.com/watch?v=HhoATZ1Imtw",
favorited: false
},
{
name: "The Comeback Kid",
artist: "Lindi Ortega",
cover: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/img/7.jpg",
source: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/mp3/7.mp3",
url: "https://www.youtube.com/watch?v=me6aoX0wCV8",
favorited: true
},
{
name: "Overdose",
artist: "Grandson",
cover: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/img/8.jpg",
source: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/mp3/8.mp3",
url: "https://www.youtube.com/watch?v=00-Rl3Jlx-o",
favorited: false
},
{
name: "Rag'n'Bone Man",
artist: "Human",
cover: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/img/9.jpg",
source: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/mp3/9.mp3",
url: "https://www.youtube.com/watch?v=L3wKzyIN1yk",
favorited: false
}]
const player = new Audio(tracks[0].source)
player.setAttribute('preload', 'metadata')
const userOptions = React.createContext({
shuffle: false,
repeat: false,
})
function Options(props){
let options = React.useContext(userOptions)
let [shuffl, setShuffle] = React.useState(options.shuffle)
let [repet, setRepeat] = React.useState(options.repeat)
let [fav, setFav] = React.useState(tracks[props.idx].favorited)
React.useEffect(() => setFav(tracks[props.idx].favorited))
function shuffle(){
options.shuffle = !options.shuffle
options.repeat = false
setShuffle(!shuffl)
setRepeat(false)
}
function repeat(){
options.repeat = !options.repeat
options.shuffle = false
setShuffle(false)
setRepeat(!repet)
}
function favorite(){
tracks[props.idx].favorited = !tracks[props.idx].favorited
setFav(tracks[props.idx].favorited)
}
function openURL(){
window.open(tracks[props.idx].url, "_blank")
}
return(
<div className="options">
{
shuffl &&
<button onClick={shuffle} className="opt" style={{color: '#147CC0'}}>
<Shuffle/>
</button>
||
<button onClick={shuffle} className="opt" >
<Shuffle/>
</button>
}
<button className="opt" onClick={openURL}>
<BoxArrowUpRight/>
</button>
{
fav &&
<button onClick={favorite} className="opt" style={{color: '#147CC0'}}>
<HeartFill/>
</button>
||
<button onClick={favorite} className="opt" >
<Heart/>
</button>
}
{
repet &&
<button onClick={repeat} className="opt" style={{color: '#147CC0'}}>
<ArrowRepeat/>
</button>
||
<button onClick={repeat} className="opt">
<ArrowRepeat/>
</button>
}
</div>
);
}
function Control(props){
return(
<div className="controls">
<button
className="controlButton"
onClick={
x => props.setIdx(props.idx-1 < 0 ? 8 : props.idx-1)
}>
<SkipBackwardFill />
</button>
{
props.playState === true ?
<button
className="centerButton"
onClick={x => props.setPlayState(false)}>
<PauseFill />
</button> :
<button
className="centerButton"
onClick={x => props.setPlayState(true)}>
<PlayFill />
</button>
}
<button
className="controlButton"
onClick={x => props.setIdx((props.idx+1)%9)}>
<SkipForwardFill />
</button>
</div>
);
}
function Progress(props){
let [currLength, setCurrLength] = React.useState(0)
let [length, setLength] = React.useState(0)
let options = React.useContext(userOptions)
const progressBar = document.querySelector('.progressBar')
function updateProgress(e){
let offset = e.target.getBoundingClientRect().left
let newOffSet = e.clientX
let newWidth = newOffSet - offset
progressBar.style.width = newWidth+"px"
let secPerPx = length / 280
player.currentTime = secPerPx * newWidth
}
setInterval(() => {
setLength(Math.ceil(player.duration))
setCurrLength(Math.ceil(player.currentTime))
let secPerPx = Math.ceil(player.duration) / 280
let newWidth = player.currentTime / secPerPx
document.querySelector('.progressBar').style.width = newWidth+"px"
if(player.currentTime === player.duration){
if(options.shuffle === true){
props.setIdx((parseInt(Math.random()*1000))%9)
}
else if(options.repeat === true){
player.play()
}
else{
props.setIdx((props.idx+1)%9)
}
}
}, 1000);
function formatTime(s){
return Number.isNaN(s) ? '0:00' : (s-(s%=60))/60+(9<s?':':':0')+s
}
return(
<div className="progress">
<div className="currentTime">
<p>{formatTime(currLength)}</p>
</div>
<div
className="progressCenter"
onClick={(e) => updateProgress(e)}>
<div className="progressBar">
</div>
</div>
<div className="songLength">
<p>{formatTime(length)}</p>
</div>
</div>
);
}
function Avatar(props){
return(
<>
<img src={tracks[props.idx].cover} className="avatar1" />
<img src={tracks[props.idx].cover} className="avatar"/>
<h4 className="name">{tracks[props.idx].artist}</h4>
<h1 className="title">{tracks[props.idx].name}</h1>
</>
);
}
function Container(){
let [idx, setIdx] = React.useState(0);
let [playState, setPlayState] = React.useState(false);
let oldIdx = React.useRef(idx)
React.useEffect(() => {
if(playState === true)
player.play()
else
player.pause()
if(idx !== oldIdx.current){
player.pause()
player.src = tracks[idx].source
player.load()
player.play()
setPlayState(true)
oldIdx.current = idx
}
})
return(
<div className="playerContaier">
<Avatar idx={idx}/>
<Progress
setIdx={setIdx}
idx={idx}
/>
<Control
setIdx={setIdx}
idx={idx}
playState={playState}
setPlayState={setPlayState}/>
<Options
setIdx={setIdx}
idx={idx}
/>
</div>
);
}
function Header(){
return(
<div className="header">
<button className="icon">
<ChevronLeft/>
</button>
<h1 className="headerText">Song</h1>
<button className="icon">
<ThreeDots/>
</button>
</div>
);
}
// root method
function Index() {
return (
<div className="container" >
<Header/>
<Container/>
</div>
);
}
ReactDOM.render(<Index />, document.getElementById("root"));
View Compiled
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.