Music app using react
Music app using react
Description
This is a simple music app which uses howler npm package to play local music kept in public directory.
React hooks used:
- useEffect: useEffect is a hook that allows you to perform side effects in functional components. Side effects can include data fetching, subscriptions, or manually changing the DOM.
- useState: In React, useState is a hook that allows functional components to have state. Before the introduction of hooks, state was only available in class components. useState enables functional components to have local state without needing to convert them to class components.
- useMemo: In React, useMemo is a hook that memoizes the result of a function so that the function is not re-executed on every render unless its dependencies change. This can help optimize performance by preventing unnecessary calculations or expensive operations.
- useRef: In React, the useRef hook is used to create a mutable reference that persists across renders of a component. Unlike state, changing the value of a ref doesn't trigger a re-render. useRef is commonly used to access DOM elements or to store mutable values without causing re-renders.
Preview:
Prerequisites#
- Knowledge of React.js and javascript
- Basic knowledge of tailwind css
Setup:
- npx create-vite musicApp and choose react template with javascript.
- move to musicApp folder and Install howler package.
- Now lets setup tailwind css in our project. First install tailwind with cmd After that let's make some changes in tailwind.config.js file. At last remove everything from index.css file and paste these lines to use tailwind. Follow this link for offical docs: tailwindcss.com/docs/guides/vite
- Now remove everything in App.jsx and code it.
- Make a component SinglesongCard.jsx
- We have put some songs in our public directory inside music folder and one image in src>>assets>>img.
App.jsx
import { useEffect, useMemo, useRef, useState } from "react";
import { Howl, Howler } from "howler";
import "./App.css";
import { Icon } from "@iconify/react";
import SinglesongCard from "./components/SinglesongCard";
function App() {
const songs = [
{ id: 1, name: "Akhiyaan gulaab", track: "./music/Akhiyaan.mp3" },
{ id: 2, name: "Husn", track: "./music/Husn.mp3" },
{ id: 3, name: "Jeena haram kar diya", track: "./music/Jeena.mp3" },
{ id: 4, name: "Teri baaton mein", track: "./music/Teri.mp3" },
];
//make hooks for handling state
const [currentSong, setCurrentSong] = useState(null);
const [soundPlayed, setSoundPlayed] = useState(null);
const [isPlaying, setIsPlaying] = useState(false);
const [currentSongIndex, setCurrentSongIndex] = useState(-1);
const [currentTime, setCurrentTime] = useState(0);
const [duration, setDuration] = useState(0);
const progressBarRef = useRef(null);
useEffect(() => {
if (currentSong) {
const sound = new Howl({ src: [currentSong.track] });
sound.play();
setSoundPlayed(sound);
setIsPlaying(true);
}
}, [currentSong]);
useEffect(() => {
if (soundPlayed) {
soundPlayed.on("play", () => {
setIsPlaying(true);
requestAnimationFrame(updateCurrentTime);
});
soundPlayed.on("load", () => {
setDuration(soundPlayed.duration());
});
soundPlayed.on("end", () => {
setIsPlaying(false);
});
}
});
const updateCurrentTime = () => {
setCurrentTime(soundPlayed.seek());
if (soundPlayed.playing()) {
requestAnimationFrame(updateCurrentTime);
}
};
const handleSongClick = (choosenSong, index) => {
console.log(choosenSong, index);
if (choosenSong) {
if (soundPlayed) {
soundPlayed.stop();
}
setCurrentSong(choosenSong);
setCurrentSongIndex(index);
}
};
const nextSong = () => {
if (currentSongIndex < songs.length - 1) {
let index = currentSongIndex + 1;
handleSongClick(songs[index], index);
} else {
handleSongClick(songs[0], 0);
}
};
const prevSong = () => {
if (currentSongIndex > 0) {
let index = currentSongIndex - 1;
handleSongClick(songs[index], index);
} else {
handleSongClick(songs[songs.length - 1], songs.length - 1);
}
};
//lets make play and pause toggle function
const togglePlay = () => {
if (soundPlayed) {
if (isPlaying) {
soundPlayed.pause();
} else {
soundPlayed.play();
}
setIsPlaying(!isPlaying);
}
};
const formatTime = (time) => {
let minutes = Math.floor(time / 60);
let second = Math.floor(time % 60);
return `${minutes}:${second < 10 ? 0 : ""}${second}`;
};
//for calculating progress of the song
const calculateProgress = () => {
let n = (currentTime / duration) * 100;
return n;
};
//progress bag click handler
const handleProgressBarClick = (e) => {
const clickedPosition = e.nativeEvent.offsetX;
const progressBarWidth = progressBarRef.current.clientWidth;
const clickedTime = (clickedPosition / progressBarWidth) * duration;
soundPlayed.seek(clickedTime);
setCurrentTime(clickedTime);
};
const icons = useMemo(() => {
return {
playpause: isPlaying ? (
) : (
),
next: ,
prev: ,
};
}, [isPlaying]);
return (
<>
<div className="container h-[550px] w-[800px] bg-black rounded-md flex overflow-hidden">
<div className="left-container flex-1 bg-[#CDE0F6] p-4 relative">
<div className="flex flex-col flex-1 h-full overflow-auto song-list">
{/* //lets add song */}
{songs.map((song, index) => (
<SinglesongCard
songIdx={index}
key={song.id}
song={song}
handleSongClick={handleSongClick}
/>
))}
</div>
</div>
<div className="right-container relative flex flex-col justify-center items-center flex-1 bg-[#47BE52] p-4">
<div className="w-full h-[60%] bg-white flex flex-col justify-center items-center">
<h1 className="font-bold text-[3rem]">Music</h1>
{currentSong && <p>{currentSong.name}</p>}
</div>
<div className="flex items-center justify-center gap-4 my-4 controls">
<button className="" onClick={prevSong}>
{icons.prev}
</button>
<button className="" onClick={togglePlay}>
{icons.playpause}
</button>
<button className="" onClick={nextSong}>
{icons.next}
</button>
</div>
<div
ref={progressBarRef}
onClick={handleProgressBarClick}
className="songprogressbar w-[90%] relative bg-white h-1 cursor-pointer my-2"
>
<p className="absolute left-0 bottom-2">
{formatTime(currentTime)}
</p>
<p className="absolute right-0 bottom-2">{formatTime(duration)}</p>
<div
className="w-0 h-1 bg-black bar"
style={{ width: calculateProgress() + "%" }}
></div>
</div>
</div>
</div>
</>
);
}
export default App;
SinglesongCard.jsx
import React from "react";
import musicImg from "../assets/img/music.png";
function SinglesongCard({ song, songIdx, handleSongClick }) {
return (
<div
onClick={() => {
handleSongClick(song, songIdx);
}}
className="songelement bg-[#444444] p-4 flex items-center text-white h-fit w-full cursor-pointer hover:bg-[#444444ad]"
>
<img src={musicImg} alt="" className="w-10 h-10 " />
<h2 className="ml-4 text-sm font-semibold">{song.name}</h2>
</div>
);
}
export default SinglesongCard;
Github repo link
https://github.com/silentvoice143/musicApp
Comments
Post a Comment