AI摘要:本项目是一个基于React的音乐平台,提供歌曲搜索、播放、随机播放和持续随机播放功能。平台界面简洁,操作直观,支持播放控制和播放列表管理。项目计划未来版本中优化功能,增强用户互动性和音乐体验。代码实现包括状态初始化、搜索功能、随机播放、持续随机播放、预加载下一首歌曲和音频播放事件监听等。

Powered by 返回门户.

我的音乐平台

本项目是一个基于React的音乐平台,用户可以在平台上进行歌曲搜索、播放以及享受随机播放功能。平台支持歌曲的播放、暂停、切换以及持续随机播放功能,提供了简洁易用的界面和良好的用户体验。以下是平台的功能介绍和实现细节。

1. 项目概述

我的音乐平台的核心功能是通过一个搜索框,用户可以搜索到自己喜欢的歌曲并播放。平台内置了随机播放和持续随机播放功能,能够根据用户需求实时播放热门音乐。平台的设计简洁,操作直观,用户可以通过搜索框、播放控制按钮等轻松操作。

您可以通过以下链接访问项目:

2. 搜索功能

平台的核心功能之一是歌曲搜索。用户可以通过输入歌曲名称来搜索相关的音乐,搜索结果将显示相关歌曲的列表,用户可以选择其中一首进行播放。平台会展示歌曲的名称、艺术家信息以及专辑封面等详细信息。

下一步目标:计划在搜索功能中加入更多过滤选项,允许用户按照艺术家、专辑等进行更精确的筛选。

前端代码:

<SearchBar onSearch={handleSearch} />

3. 随机播放功能

平台支持随机播放功能,用户点击“随机播放飙升音乐”按钮后,平台会自动选择一首随机的热门歌曲并开始播放。该功能帮助用户发现新歌,并能随时体验到不同风格的音乐。

下一步目标:进一步优化随机播放逻辑,允许用户根据音乐类别、风格进行筛选,提升音乐推荐的精准度。

前端代码:

<button onClick={handleRandomPlay} className="random-play-button">
    随机播放飙升音乐
</button>

4. 持续随机播放功能

除了普通的随机播放外,平台还提供了持续随机播放的选项。启用此功能后,平台将在当前歌曲播放结束后自动播放下一首随机歌曲,保证用户可以不断地听到新歌,提升音乐体验。

下一步目标:实现持续随机播放的优化功能,确保无缝播放并在歌曲之间减少加载时间,提升用户的听歌体验。

前端代码:

<button
    onClick={handleContinuousRandomPlay}
    className="continuous-random-play-button"
>
    {isContinuousRandom ? '停止持续随机播放' : '持续随机播放'}
</button>

5. 播放器组件

平台内的播放器组件提供了基本的播放控制功能,包括播放、暂停、上一首、下一首等操作。用户可以通过播放器随时控制音乐的播放进度,并且可以查看当前播放的歌曲信息。

下一步目标:优化播放器界面,增加歌词显示、音量调节等功能,使其更加全面和实用。

前端代码:

<Player
    song={selectedSong}
    ref={audioRef}
    onPrevious={handlePrevious}
    onNext={handleNext}
    hasPrevious={currentIndex > 0}
    hasNext={true} // 始终显示“下一首”按钮
/>

6. 播放列表管理

平台支持播放列表功能,每次选择歌曲播放时,平台会将该歌曲添加到播放列表中。用户可以通过控制按钮来浏览整个播放列表,切换到之前或之后的歌曲,享受更加自由的音乐播放体验。

下一步目标:在播放列表中加入歌曲删除和排序功能,让用户可以自由管理播放列表中的歌曲。

前端代码:

<SongList songs={songs} onSelect={handleSelectSong} />

7. 弹窗与交互

为了提升用户体验,平台中的某些功能,如加载提示、错误提示等,都会使用弹窗进行展示。这些弹窗信息直接反馈给用户,帮助用户快速了解当前操作状态。

8. 小结

我的音乐平台通过简洁、直观的设计提供了强大的功能,用户可以方便地进行歌曲搜索、播放,并体验到随机播放和持续随机播放等创新功能。随着平台的不断发展,我们计划在后续版本中进一步优化现有功能,增强用户的互动性和音乐体验。


实现代码

明白了,我会按照你提供的 scode 格式来展示解释。下面是分段拆分的代码展示和对应的 scode 文字说明。


1. 状态初始化与组件导入

import { useState, useEffect, useRef, useCallback } from 'react';
import SearchBar from '../components/SearchBar';
import SongList from '../components/SongList';
import Player from '../components/Player';

过程:在这一部分,导入了 React 的几个基本钩子(useStateuseEffectuseRefuseCallback),以及自定义的组件 SearchBarSongListPlayer,它们分别用于处理搜索框、歌曲列表和播放器功能的展示与交互。


2. 状态管理

const [songs, setSongs] = useState([]);
const [selectedSong, setSelectedSong] = useState(null);
const [nextSong, setNextSong] = useState(null); // 预加载的下一首歌曲
const [nextSongLoading, setNextSongLoading] = useState(false); // 是否正在预加载
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);

const [isContinuousRandom, setIsContinuousRandom] = useState(false); // 持续随机播放状态

const [playlist, setPlaylist] = useState([]); // 播放列表
const [currentIndex, setCurrentIndex] = useState(-1); // 当前歌曲在播放列表中的索引

const audioRef = useRef(null);

过程:这一段代码定义了多个 state,包括歌曲列表、当前播放的歌曲、下一首预加载歌曲、加载状态、错误信息等。通过这些状态,组件能够管理和更新音乐播放器的行为,比如显示正在加载、播放列表、当前歌曲等信息。


3. 搜索功能实现

const handleSearch = async (query) => {
    setLoading(true);
    setError(null);
    setSelectedSong(null); // 重置选中的歌曲
    setPlaylist([]); // 清空播放列表
    setCurrentIndex(-1); // 重置当前索引
    try {
        // 搜索API调用(已去除具体实现)
        if (response.data.code !== 0) {
            setError(response.data.msg || '搜索失败');
            setSongs([]);
            setLoading(false);
            return;
        }

        // 确保返回的数据是数组
        const data = response.data.data;
        if (Array.isArray(data)) {
            setSongs(data);
        } else {
            setSongs([data]);
        }
    } catch (err) {
        console.error(err);
        setError('服务器错误');
        setSongs([]);
    } finally {
        setLoading(false);
    }
};

过程:handleSearch 函数在用户进行搜索时被调用。它会清空当前选择的歌曲和播放列表,显示加载状态,并向后端发起请求获取歌曲数据。成功时,将返回的歌曲列表存储在 songs 状态中,失败时显示错误信息。这里的 API 调用已去除,重点在于状态管理和错误处理。


4. 选择歌曲播放

const handleSelectSong = async (index) => {
    setLoading(true);
    setError(null);
    try {
        const selected = songs[index - 1];
        if (!selected) {
            setError('未找到选中的歌曲');
            setLoading(false);
            return;
        }

        // 获取歌曲详情(已去除具体实现)
        setSelectedSong(songDetail);

        // 更新播放列表和当前索引
        setPlaylist((prevPlaylist) => {
            const newPlaylist = [...prevPlaylist, songDetail];
            setCurrentIndex(newPlaylist.length - 1);
            return newPlaylist;
        });
    } catch (err) {
        console.error(err);
        setError('服务器错误');
        setSelectedSong(null);
    } finally {
        setLoading(false);
    }
};

过程:handleSelectSong 函数负责处理用户选择某一首歌曲播放。它会根据选择的歌曲更新播放列表和当前歌曲的状态。如果选择的歌曲有效,则将歌曲详情存储在 selectedSong 中,并更新播放列表。这里的 API 调用已去除,焦点在状态的更新和错误处理。


5. 随机播放功能实现

const handleRandomPlay = useCallback(async () => {
    setLoading(true);
    setError(null);

    try {
        // 获取随机飙升歌曲(已去除具体实现)
        const { name: songName, auther } = trendingResponse.data.info;

        // 搜索匹配的歌曲(已去除具体实现)
        const matchedSong = songsArray.find(song => song.songname === songName && song.name === auther);

        if (!matchedSong) {
            setError('未能找到匹配的歌曲');
            setLoading(false);
            return;
        }

        // 获取歌曲详情(已去除具体实现)
        setSelectedSong(songDetail);
        setPlaylist((prevPlaylist) => [...prevPlaylist, songDetail]);
    } catch (err) {
        console.error(err);
        setError('服务器错误');
    } finally {
        setLoading(false);
    }
}, []);

过程:handleRandomPlay 函数实现了随机播放飙升音乐的功能。它首先尝试获取一首随机歌曲,并通过搜索与歌曲名和作者匹配的结果。如果匹配到歌曲,更新 selectedSong 和播放列表。如果无法找到匹配的歌曲,则显示错误信息。API 调用的部分已去除,重点在于歌曲匹配和状态管理。


6. 持续随机播放功能

const handleContinuousRandomPlay = () => {
    const newStatus = !isContinuousRandom;
    setIsContinuousRandom(newStatus);

    if (newStatus) {
        // 开启持续随机播放,立即播放一首随机歌曲
        handleRandomPlay();
    } else {
        // 关闭持续随机播放,清除预加载的下一首歌曲
        setNextSong(null);
    }
};

过程:handleContinuousRandomPlay 函数切换持续随机播放的状态。如果启用持续播放,则会立即播放一首随机歌曲;否则,会关闭此功能并清空下一首预加载歌曲的状态。这个功能依赖于 isContinuousRandom 状态的切换。


7. 预加载下一首歌曲

const preloadNextRandomSong = useCallback(async () => {
    if (nextSongLoading) return;

    setNextSongLoading(true);

    try {
        // 重用 handleRandomPlay 中的逻辑,但不更新 selectedSong,而是更新 nextSong
        setNextSong(songDetail);
    } catch (err) {
        console.error('预加载下一首歌曲失败', err);
    } finally {
        setNextSongLoading(false);
    }
}, [nextSongLoading]);

过程:preloadNextRandomSong 函数用于预加载下一首随机歌曲。它与 handleRandomPlay 的逻辑相似,但只更新 nextSong 状态,而不更新当前播放的歌曲。当下一首歌曲加载完成后,将显示该歌曲信息。


8. 音频播放事件监听

useEffect(() => {
    if (!isContinuousRandom) return;

    const audioElement = audioRef.current;
    if (!audioElement) return;

    const handleTimeUpdate = () => {
        if (
            audioElement.duration - audioElement.currentTime < 10 &&
            !nextSongLoading &&
            !nextSong
        ) {
            preloadNextRandomSong();
        }
    };

    const handleEnded = () => {
        handleNext();
    };

    audioElement.addEventListener('timeupdate', handleTimeUpdate);
    audioElement.addEventListener('ended', handleEnded);

    return () => {
        audioElement.removeEventListener('timeupdate', handleTimeUpdate);
        audioElement.removeEventListener('ended', handleEnded);
    };
}, [
    isContinuousRandom,
    selectedSong,
    nextSong,
    nextSongLoading,
    preloadNextRandomSong,
    handleRandomPlay,
]);

过程:useEffect 钩子监听音频播放的 timeupdateended 事件。当播放进度接近结束时,自动预加载下一首随机歌曲。如果歌曲播放结束,自动播放下一首歌曲。


9. 上一首和下一首歌曲

const handlePrevious = () => {
    if (currentIndex > 0) {
        const newIndex = currentIndex - 1;
        setCurrentIndex(newIndex);
        setSelectedSong(playlist[newIndex]);
    }
};

const handleNext = () => {
    if (currentIndex < playlist.length - 1) {
        // 播放列表中有下一首歌曲
        const newIndex = currentIndex + 1;
        setCurrentIndex(newIndex);
        setSelectedSong(playlist

[newIndex]);
    }
};

过程:handlePrevioushandleNext 函数分别处理播放上一首和下一首歌曲的功能。它们根据当前歌曲在播放列表中的索引进行相应的更新。


2024-11-13T16:54:05.png2024-11-13T16:54:25.png2024-11-13T16:54:57.png

最后修改:2024 年 11 月 14 日
如果觉得我的文章挺有趣,赞赏一杯小奶茶
END
本文作者:
文章标题:我的音乐平台
本文地址:https://maxtral.fun/index.php/archives/167/
版权说明:若无注明,本文皆MAXBROSER原创,转载请保留文章出处。