React 构建一个音乐播放器

React 构建一个音乐播放器

“音乐播放器”项目是一个使用React构建的web应用程序,为用户提供播放、暂停和管理音乐收藏的界面。它具有响应式设计,使用户可以轻松地享受自己的歌曲。它有一个单独的数据文件,用户可以使用该文件将自己的歌曲添加到列表中,并且可以听自己个性化的歌曲播放列表。

最终输出的预览: 让我们来看一下最终输出的样子。

React 构建一个音乐播放器

媒体播放器应用的先决条件和使用的技术

音乐播放器的方法和功能

音乐播放器项目包括以下功能和方法:

  1. 用户友好的界面:该项目展示了一个界面,带有控制按钮,用于播放、暂停、调整音量和跟踪进度。
  2. 音乐库管理:用户可以轻松管理自己的音乐库,添加或删除歌曲并选择要播放的曲目。
  3. 音频控制:应用程序提供播放、暂停和控制音量级别的选项。
  4. 曲目进度显示:用户可以轻松跟踪正在播放的歌曲的进度。

项目设计响应式。在桌面电脑和移动设备上都能有效运行。

使用React创建音乐播放器的步骤

步骤1: 使用以下命令创建一个新的React JS项目

npx create-react-app <<Project_Name>>

步骤2: 切换到项目目录

cd word-letter-counter <<Project_Name>>

步骤3: 使用以下命令安装此项目所需的一些npm包:

npm install --save @fortawesome/react-fontawesome  
npm install --save @fortawesome/free-solid-svg-icons  
npm install sass

项目结构:

React 构建一个音乐播放器

更新的依赖关系在< strong> package.json **中,将会如下所示:

 "dependencies": {  
    "@fortawesome/free-solid-svg-icons": "^6.4.2",  
    "@fortawesome/react-fontawesome": "^0.2.0",  
    "@testing-library/jest-dom": "^5.17.0",  
    "@testing-library/react": "^13.4.0",  
    "@testing-library/user-event": "^13.5.0",  
    "react": "^18.2.0",  
    "react-dom": "^18.2.0",  
    "react-scripts": "5.0.1",  
    "sass": "^1.68.0",  
    "web-vitals": "^2.1.4"  
}  

示例: 将以下代码写入相应的文件中

这两个文件 App.jsdata.js 将位于 src 文件夹中

// FileName: App.js 
import { useRef, useState } from "react"; 
import Player from "./components/PlayerSong"; 
import Song from "./components/Song"; 
import "./styles/app.scss"; 
  
// Importing DATA 
import data from "./data"; 
import Library from "./components/Library"; 
import Nav from "./components/Navb"; 
function App() { 
  const [songs, setSongs] = useState(data()); 
  const [currentSong, setCurrentSong] = useState(songs[0]); 
  const [isPlaying, setIsPlaying] = useState(false); 
  const [libraryStatus, setLibraryStatus] = useState(false); 
  const audioRef = useRef(null); 
  const [songInfo, setSongInfo] = useState({ 
    currentTime: 0, 
    duration: 0, 
    animationPercentage: 0, 
  }); 
  const timeUpdateHandler = (e) => { 
    const current = e.target.currentTime; 
    const duration = e.target.duration; 
    //calculating percentage 
    const roundedCurrent = Math.round(current); 
    const roundedDuration = Math.round(duration); 
    const animation = Math.round((roundedCurrent / roundedDuration) * 100); 
    console.log(); 
    setSongInfo({ 
      currentTime: current, 
      duration, 
      animationPercentage: animation, 
    }); 
  }; 
  const songEndHandler = async () => { 
    let currentIndex = songs.findIndex((song) => song.id === currentSong.id); 
  
    await setCurrentSong(songs[(currentIndex + 1) % songs.length]); 
  
    if (isPlaying) audioRef.current.play(); 
  }; 
  return ( 
    <div> 
      <Nav libraryStatus={libraryStatus} setLibraryStatus={setLibraryStatus} /> 
      <Song currentSong={currentSong} /> 
      <Player 
        id={songs.id} 
        songs={songs} 
        songInfo={songInfo} 
        setSongInfo={setSongInfo} 
        audioRef={audioRef} 
        isPlaying={isPlaying} 
        setIsPlaying={setIsPlaying} 
        currentSong={currentSong} 
        setCurrentSong={setCurrentSong} 
        setSongs={setSongs} 
      /> 
      <Library 
        libraryStatus={libraryStatus} 
        setLibraryStatus={setLibraryStatus} 
        setSongs={setSongs} 
        isPlaying={isPlaying} 
        audioRef={audioRef} 
        songs={songs} 
        setCurrentSong={setCurrentSong} 
      /> 
      <audio 
        onLoadedMetadata={timeUpdateHandler} 
        onTimeUpdate={timeUpdateHandler} 
        src={currentSong.audio} 
        ref={audioRef} 
        onEnded={songEndHandler} 
      ></audio> 
    </div> 
  ); 
} 
  
export default App; 
// FileName: data.js 
  
import { v4 as uuidv4 } from "uuid"; 
function chillHop() { 
  return [ 
    { 
      name: "Sunrise Serenade", 
      cover: 
"https://media.geeksforgeeks.org/wp-content/uploads/20210224040124/JSBinCollaborativeJavaScriptDebugging6.png", 
      artist: " Harmony Harp", 
      audio: 
"https://media.geeksforgeeks.org/wp-content/uploads/20231004185212/Jawan-Prevue-Theme.mp3", 
      color: ["#205950", "#2ab3bf"], 
      id: uuidv4(), 
      active: true, 
    }, 
    { 
      name: "Urban Groove", 
      cover: 
        "https://media.geeksforgeeks.org/wp-content/uploads/20231004210806/DemotivationalPosterfull936506.jpg", 
      artist: "Beatmaster B", 
      audio: 
        "https://media.geeksforgeeks.org/wp-content/uploads/20231004184006/SoundHelix-Song-10.mp3", 
      color: ["#EF8EA9", "#ab417f"], 
      id: uuidv4(), 
      active: false, 
    }, 
    { 
      name: "Mystic Echo", 
      cover: 
"https://media.geeksforgeeks.org/wp-content/uploads/20231004210619/3408428b23c516b1687c748cb7de7be7.webp", 
      artist: " Harmony Harp", 
      audio: 
"https://media.geeksforgeeks.org/wp-content/uploads/20231004185212/Jawan-Prevue-Theme.mp3", 
      color: ["#CD607D", "#c94043"], 
      id: uuidv4(), 
      active: false, 
    }, 
    { 
      name: "Electro Vibes", 
      cover: 
"https://media.geeksforgeeks.org/wp-content/uploads/20231004184219/gfglogo0.jpg", 
      artist: "Synthwave Sensation", 
      audio: 
"https://media.geeksforgeeks.org/wp-content/uploads/20231004191840/Zinda-Banda---Jawan-(1).mp3", 
      color: ["#EF8EA9", "#ab417f"], 
      id: uuidv4(), 
      active: false, 
    }, 
    { 
      name: "Jazzy Whispers", 
      cover: 
"https://media.geeksforgeeks.org/wp-content/uploads/20231004210806/DemotivationalPosterfull936506.jpg", 
      artist: "Smooth Sax Serenade", 
      audio: 
"https://media.geeksforgeeks.org/wp-content/uploads/20231004184006/SoundHelix-Song-10.mp3", 
      color: ["#CD607D", "#c94043"], 
      id: uuidv4(), 
      active: false, 
    }, 
    { 
      name: "Tropical Breez", 
      cover: 
"https://media.geeksforgeeks.org/wp-content/uploads/20231004210619/3408428b23c516b1687c748cb7de7be7.webp", 
      artist: "Island Rhythms", 
      audio: 
"https://media.geeksforgeeks.org/wp-content/uploads/20231004191840/Zinda-Banda---Jawan-(1).mp3", 
      color: ["#205950", "#2ab3bf"], 
      id: uuidv4(), 
      active: false, 
    }, 
  ]; 
} 
  
export default chillHop; 

这五个文件 Library.jsLibrarySong.jsPlayerSong.jsNavb.jsSong.js 将位于 src 文件夹下的 components 文件夹中。

// FileName: Library.js 
  
import React from "react"; 
import LibrarySong from "./LibrarySong"; 
  
const Library = ({ 
    songs, 
    setCurrentSong, 
    audioRef, 
    isPlaying, 
    setSongs, 
    setLibraryStatus, 
    libraryStatus, 
}) => { 
    return ( 
        <div className={`library ${libraryStatus ? "active" : ""}`}> 
            <h2 style={{ color: "white" }}>All songs</h2> 
            <div className="library-songs"> 
                {songs.map((song) => ( 
                    <LibrarySong 
                        setSongs={setSongs} 
                        isPlaying={isPlaying} 
                        audioRef={audioRef} 
                        songs={songs} 
                        song={song} 
                        setCurrentSong={setCurrentSong} 
                        id={song.id} 
                        key={song.id} 
                    /> 
                ))} 
            </div> 
        </div> 
    ); 
}; 
  
export default Library; 
// FileName: LibrarySong.js 
  
import React from "react"; 
const LibrarySong = ({ 
    song, 
    songs, 
    setCurrentSong, 
    audioRef, 
    isPlaying, 
    setSongs, 
    id, 
}) => { 
    const songSelectHandler = async () => { 
        await setCurrentSong(song); 
        //active 
        const newSongs = songs.map((song) => { 
            if (song.id === id) { 
                return { 
                    ...song, 
                    active: true, 
                }; 
            } else { 
                return { 
                    ...song, 
                    active: false, 
                }; 
            } 
        }); 
        setSongs(newSongs); 
        //check if song is playing 
        if (isPlaying) audioRef.current.play(); 
    }; 
    return ( 
        <div 
            onClick={songSelectHandler} 
            className={`library-song ${song.active ? "selected" : ""}`} 
        > 
            <img src={song.cover} alt={song.name} /> 
            <div className="song-description"> 
                <h3>{song.name}</h3> 
                <h4>{song.artist}</h4> 
            </div> 
        </div> 
    ); 
}; 
  
export default LibrarySong; 
// FileName: Navb.js 
  
import React from "react"; 
  
const Nav = ({ setLibraryStatus, libraryStatus }) => { 
    return ( 
        <nav> 
            <h1>GeeksforGeeks Music Player</h1> 
            <button 
                onClick={() => { 
                    setLibraryStatus(!libraryStatus); 
                }} 
            > 
                <h4>Library</h4> 
            </button> 
        </nav> 
    ); 
}; 
  
export default Nav; 
// FileName: PlayerSong.js 
  
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 
import { 
    faPlay, 
    faAngleLeft, 
    faAngleRight, 
    faPause, 
} from "@fortawesome/free-solid-svg-icons"; 
  
const Player = ({ 
    currentSong, 
    isPlaying, 
    setIsPlaying, 
    audioRef, 
    setSongInfo, 
    songInfo, 
    songs, 
    setCurrentSong, 
    id, 
    setSongs, 
}) => { 
    //useEffect 
    const activeLibraryHandler = (nextPrev) => { 
        const newSongs = songs.map((song) => { 
            if (song.id === nextPrev.id) { 
                return { 
                    ...song, 
                    active: true, 
                }; 
            } else { 
                return { 
                    ...song, 
                    active: false, 
                }; 
            } 
        }); 
        setSongs(newSongs); 
        console.log("Hey from useEffect form player JS"); 
    }; 
    //Event Handlers 
    const dragHandler = (e) => { 
        audioRef.current.currentTime = e.target.value; 
        setSongInfo({ ...songInfo, currentTime: e.target.value }); 
    }; 
    const playSongHandler = () => { 
        if (isPlaying) { 
            audioRef.current.pause(); 
            setIsPlaying(!isPlaying); 
        } else { 
            audioRef.current.play(); 
            setIsPlaying(!isPlaying); 
        } 
    }; 
  
    const getTime = (time) => 
        Math.floor(time / 60) + ":" + ("0" + Math.floor(time % 60)).slice(-2); 
    const skipTrackHandler = async (direction) => { 
        let currentIndex = songs.findIndex( 
            (song) => song.id === currentSong.id 
        ); 
        if (direction === "skip-forward") { 
            await setCurrentSong(songs[(currentIndex + 1) % songs.length]); 
            activeLibraryHandler(songs[(currentIndex + 1) % songs.length]); 
        } 
        if (direction === "skip-back") { 
            if ((currentIndex - 1) % songs.length === -1) { 
                await setCurrentSong(songs[songs.length - 1]); 
                // playAudio(isPlaying, audioRef); 
                activeLibraryHandler(songs[songs.length - 1]); 
  
                return; 
            } 
            await setCurrentSong(songs[(currentIndex - 1) % songs.length]); 
            activeLibraryHandler(songs[(currentIndex - 1) % songs.length]); 
        } 
        if (isPlaying) audioRef.current.play(); 
    }; 
    //adding the styles 
    const trackAnim = { 
        transform: `translateX({songInfo.animationPercentage}%)`, 
    }; 
    return ( 
        <div className="player"> 
            <div className="time-control"> 
                <p>{getTime(songInfo.currentTime)}</p> 
                <div 
                    style={{ 
                        background:  
`linear-gradient(to right,{currentSong.color[0]}, ${currentSong.color[1]})`, 
                    }} 
                    className="track"
                > 
                    <input 
                        min={0} 
                        max={songInfo.duration || 0} 
                        value={songInfo.currentTime} 
                        onChange={dragHandler} 
                        type="range"
                    /> 
                    <div style={trackAnim} className="animate-track"></div> 
                </div> 
                <p> 
                    {songInfo.duration ? getTime(songInfo.duration) : "00:00"} 
                </p> 
            </div> 
            <div className="play-control"> 
                <FontAwesomeIcon 
                    onClick={() => skipTrackHandler("skip-back")} 
                    size="2x"
                    className="skip-back"
                    icon={faAngleLeft} 
                /> 
                {!isPlaying ? ( 
                    <FontAwesomeIcon 
                        onClick={playSongHandler} 
                        size="2x"
                        className="play"
                        icon={faPlay} 
                    /> 
                ) : ( 
                    <FontAwesomeIcon 
                        onClick={playSongHandler} 
                        size="2x"
                        className="pause"
                        icon={faPause} 
                    /> 
                )} 
  
                <FontAwesomeIcon 
                    onClick={() => skipTrackHandler("skip-forward")} 
                    size="2x"
                    className="skip-forward"
                    icon={faAngleRight} 
                /> 
            </div> 
        </div> 
    ); 
}; 
  
export default Player; 
// FileName: Song.js 
  
import React from "react"; 
  
const Song = ({ currentSong }) => { 
    return ( 
        <div className="song-container"> 
            <img src={currentSong.cover} alt={currentSong.name} /> 
            <h2>{currentSong.name}</h2> 
            <h3>{currentSong.artist}</h3> 
        </div> 
    ); 
}; 
  
export default Song; 

这五个文件 library.scssapp.scssnav.scssplayer.scsssong.scss 将位于 src 文件夹下的 styles 文件夹中。

CSS

/* FileName: app.scss */
  
* { 
    margin: 0; 
    padding: 0; 
    box-sizing: border-box; 
    font-family: "Gilroy", sans-serif; 
} 
body { 
    background: rgb(231, 235, 214); 
    background: linear-gradient( 
        180deg, 
        rgba(231, 235, 214, 1) 0%, 
        rgba(55, 102, 44, 1) 100%
    ); 
} 
  
@import "./library"; 
@import "./player"; 
@import "./song"; 
@import "./nav"; 
h2, 
h3 { 
    color: #133a1b; 
} 
h3, 
h4 { 
    font-weight: 600; 
} 
button { 
    font-weight: 700; 
} 

CSS

/* FileName: library.scss */
  
.library { 
    position: fixed; 
    top: 0; 
    left: 0; 
    width: 20rem; 
    height: 100%; 
    background: #32522b; 
    box-shadow: 2px 2px 50px rgba(0, 0, 0, 0.205); 
    overflow: scroll; 
    transform: translateX(-100%); 
    transition: all 0.2s ease; 
    opacity: 0; 
    h2 { 
        padding: 2rem; 
    } 
} 
.library-song { 
    display: flex; 
    align-items: center; 
    padding: 1rem 2rem 1rem 2rem; 
    img { 
        width: 30%; 
    } 
    &:hover { 
        background: rgb(120, 248, 160); 
    } 
} 
.song-description { 
    padding-left: 1rem; 
    h3 { 
        color: #ffffff; 
        font-size: 1rem; 
    } 
    h4 { 
        color: gray; 
        font-size: 0.7rem; 
    } 
} 
::-webkit-scrollbar { 
    width: 5px; 
} 
::-webkit-scrollbar-thumb { 
    background: rgb(255, 183, 183); 
    border-radius: 10px; 
} 
  
::-webkit-scrollbar-track { 
    background: rgb(221, 221, 221); 
} 
.selected { 
    background: rgb(255, 230, 255); 
    h3 { 
        color: #306b26; 
    } 
} 
.active { 
    transform: translateX(0%); 
    opacity: 1; 
} 
@media screen and (max-width: 768px) { 
    .library { 
        width: 100%; 
    } 
} 

CSS

/*FileName: nav.scss */
  
h1, 
h4 { 
    color: rgb(9, 70, 9); 
} 
  
nav { 
    min-height: 10vh; 
    display: flex; 
    justify-content: center; 
    align-items: center; 
    margin: 20px; 
    button { 
        background: transparent; 
        border: none; 
        cursor: pointer; 
        font-size: 16px; 
        margin-left: 20%; 
        border: 2px solid rgb(41, 216, 25); 
        padding: 0.8rem; 
        transition: all 0.3s ease; 
        &:hover { 
            background: rgb(89, 219, 77); 
            color: white; 
        } 
    } 
} 
@media screen and (max-width: 768px) { 
    nav { 
        button { 
            z-index: 10; 
        } 
    } 
} 

CSS

/* FileName: player.scss */
  
.player { 
    min-height: 20vh; 
    display: flex; 
    flex-direction: column; 
    align-items: center; 
    justify-content: space-between; 
} 
.time-control { 
    width: 50%; 
    display: flex; 
    align-items: center; 
    input { 
        width: 100%; 
        background-color: transparent; 
        cursor: pointer; 
    } 
    p { 
        padding: 1rem; 
        font-weight: 700; 
    } 
} 
.play-control { 
    display: flex; 
    justify-content: space-between; 
    align-items: center; 
    padding: 1rem; 
    width: 30%; 
    svg { 
        cursor: pointer; 
    } 
} 
input[type="range"]:focus { 
    outline: none; 
} 
input[type="range"]::-webkit-slider-thumb { 
    -webkit-appearance: none; 
    height: 16px; 
    width: 16px; 
} 
.track { 
    background: lightblue; 
    width: 100%; 
    height: 1rem; 
    position: relative; 
    border-radius: 1rem; 
    overflow: hidden; 
} 
.animate-track { 
    background: rgb(204, 204, 204); 
    width: 100%; 
    height: 100%; 
    position: absolute; 
    top: 0; 
    left: 0; 
    transform: translateX(0%); 
    pointer-events: none; 
} 
@media screen and (max-width: 768px) { 
    .time-control { 
        width: 90%; 
    } 
    .play-control { 
        width: 80%; 
    } 
} 

CSS

/* FileName: song.scss */
  
.song-container { 
    min-height: 60vh; 
    display: flex; 
    flex-direction: column; 
    align-items: center; 
    justify-content: center; 
    img { 
        width: 20%; 
        // 
    } 
    h2 { 
        padding: 3rem 1rem 1rem 1rem; 
    } 
    h3 { 
        font-size: 1rem; 
    } 
} 
@media screen and (max-width: 768px) { 
    .song-container { 
        img { 
            width: 60%; 
        } 
    } 
} 
@keyframes rotate { 
    from { 
        transform: rotate(0deg); 
    } 
    to { 
        transform: rotate(360deg); 
    } 
} 
  
.rotateSong { 
    animation: rotate 20s linear forwards infinite; 
} 

运行项目的步骤:

步骤1: 在终端中输入以下命令

npm start

步骤2: 打开网络浏览器并输入以下URL

http://localhost:3000/

输出:

React 构建一个音乐播放器

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程