fix(AudioPlayer):

1. Fix next handle when last song;
2. Close ssr;
mack-mac
mackt 7 months ago
parent e452b61bd7
commit 40fffcda82

@ -14,25 +14,26 @@ import PlayerControl from './PlayerControl';
import useAudioStore from '@/store/audio'; import useAudioStore from '@/store/audio';
export default function AudioPlayer({ className }: { className?: string }) { export default function AudioPlayer({ className }: { className?: string }) {
const audioRef = useRef(new Audio()); const { audio, switchSongByDiff } = useAudioStore(
const [trackProgress, setTrackProgress] = useState<number>(0); // 音频进度 useShallow((state) => {
const isReady = useRef<boolean>(false); // 是否完成加载 return {
const { duration } = audioRef.current; // 音频总时长;
const { playStatus, setPlayStatus, audio, switchSongByDiff } = useAudioStore(
useShallow((state) => ({
playStatus: state.playStatus,
setPlayStatus: state.setPlayStatus,
audio: state.audio, audio: state.audio,
playList: state.playList, playList: state.playList,
switchSongByDiff: state.switchSongByDiff, switchSongByDiff: state.switchSongByDiff,
})), };
}),
); );
// const musicPlayers = useRef<HTMLAudioElement | undefined>(typeof Audio !== 'undefined' ? new Audio('') : undefined);
const audioRef = useRef(new Audio(''));
const [isPlaying, setIsPlaying] = useState(false); // 播放状态
const [trackProgress, setTrackProgress] = useState<number>(0); // 音频进度(s)
const isFirst = useRef<boolean>(false); // 第一次进入
const { duration } = audioRef.current; // 音频总时长(s)
// 播放/暂停 // 播放/暂停
const handlePlay = () => { const handlePlay = () => {
setPlayStatus(!playStatus); setIsPlaying(!isPlaying);
}; };
// 上一首 // 上一首
@ -45,47 +46,47 @@ export default function AudioPlayer({ className }: { className?: string }) {
switchSongByDiff(1); switchSongByDiff(1);
}; };
// 调整进度 // 调整播放进度
const handleChangeProgress = (value: string) => { const handleChangeProgress = (value: string) => {
audioRef.current.currentTime = Number(value); audioRef.current.currentTime = Number(value);
setTrackProgress(audioRef.current.currentTime); setTrackProgress(audioRef.current.currentTime);
}; };
// 给 audio 绑定监听 // 监听音频进度
const onTimeUpdate = () => {
if (audioRef.current.ended) handlePlayNext();
setTrackProgress(audioRef.current.currentTime);
};
// 给 audio 绑定 timeupdate 监听
const handleTimeUpdate = (status: boolean) => { const handleTimeUpdate = (status: boolean) => {
status status
? audioRef.current.addEventListener('timeupdate', onTimeUpdate) ? audioRef.current.addEventListener('timeupdate', onTimeUpdate)
: audioRef.current.removeEventListener('timeupdate', onTimeUpdate); : audioRef.current.removeEventListener('timeupdate', onTimeUpdate);
}; };
// 监听进度 // 播放/暂停
const onTimeUpdate = () => {
if (audioRef.current.ended) handlePlayNext();
setTrackProgress(audioRef.current.currentTime);
};
// 监听播放/暂停
useEffect(() => { useEffect(() => {
playStatus ? audioRef.current.play() : audioRef.current.pause(); isPlaying ? audioRef.current.play() : audioRef.current.pause();
}, [playStatus]); }, [isPlaying]);
// 监听切换歌曲 // 切换歌曲
useEffect(() => { useEffect(() => {
console.log('触发');
handleTimeUpdate(false);
audioRef.current.pause(); audioRef.current.pause();
if (!audio) return; if (!audio) return;
audioRef.current = new Audio(audio.src); audioRef.current = new Audio(audio.src);
setTrackProgress(audioRef.current.currentTime);
if (isReady.current) { console.log('id变化', { audio, isFirst: isFirst.current });
if (isFirst.current) {
audioRef.current.play(); audioRef.current.play();
setPlayStatus(true); setIsPlaying(true);
handleTimeUpdate(true); handleTimeUpdate(true);
} else { } else {
isReady.current = true; isFirst.current = true;
} }
}, [audio]); }, [audio]);
// 卸载时的处理
useEffect(() => { useEffect(() => {
return () => { return () => {
audioRef.current.pause(); audioRef.current.pause();
@ -96,7 +97,7 @@ export default function AudioPlayer({ className }: { className?: string }) {
return ( return (
<div> <div>
<PlayerControl <PlayerControl
playStatus={playStatus} playStatus={isPlaying}
audio={audio} audio={audio}
duration={duration} duration={duration}
onPlay={handlePlay} onPlay={handlePlay}

@ -1,11 +1,13 @@
'use client'; 'use client';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import dynamic from 'next/dynamic';
import Player from './Player'; import Player from './Player';
import { debounce } from 'lodash'; // import { debounce } from 'lodash';
export default function PlayerBar({ className }: { className?: string }) { const PlayerBar = ({ className }: { className?: string }) => {
let oldScrollY: number = 0; let oldScrollY: number = 0;
function handleScroll() { function handleScroll() {
if (window.scrollY > oldScrollY) { if (window.scrollY > oldScrollY) {
@ -40,4 +42,6 @@ export default function PlayerBar({ className }: { className?: string }) {
</div> </div>
</div> </div>
); );
} };
export default dynamic(() => Promise.resolve(PlayerBar), { ssr: false });

@ -15,10 +15,8 @@ interface Props {
} }
export default function JournalItem({ listId, songList, className }: Props) { export default function JournalItem({ listId, songList, className }: Props) {
const { playStatus, setPlayStatus, setPlayList, audio, playList, playListId, setAudio } = useAudioStore( const { setPlayList, audio, playList, playListId, setAudio } = useAudioStore(
useShallow((state) => ({ useShallow((state) => ({
playStatus: state.playStatus,
setPlayStatus: state.setPlayStatus,
setplayList: state.setPlayList, setplayList: state.setPlayList,
audio: state.audio, audio: state.audio,
setAudio: state.setAudio, setAudio: state.setAudio,

@ -9,29 +9,29 @@ interface AuioState {
playStatus: boolean; playStatus: boolean;
/** 播放列表id */ /** 播放列表id */
playListId: string; playListId: string;
/** 修改播放列表id */
setPlayListId: (id: string) => void;
/** 播放器显示状态 */ /** 播放器显示状态 */
show: boolean; show: boolean;
/** 正在播放的音频信息 */ /** 正在播放的音频信息 */
audio: SongInfo | null; audio: SongInfo | null;
/** /** 播放列表 */
playList: Array<SongInfo>;
/**
* @description
* @param id id playList * @param id id playList
*/ */
setAudio: (id: string | null) => void; setAudio: (id: string | null) => void;
/** 播放列表 */ /** @description 显示/隐藏播放器 */
playList: Array<SongInfo>;
/** 播放/暂停 */
setPlayStatus: (value: boolean) => void;
/** 显示/隐藏播放器 */
setShow: (value: boolean) => void; setShow: (value: boolean) => void;
/** 设置播放列表 */ /** @description 设置播放列表 */
setPlayList: (params: { id: string; list: SongInfo[] }) => void; setPlayList: (params: { id: string; list: SongInfo[] }) => void;
/** 获取歌曲信息 */ /** @description 获取歌曲信息 */
getSongInfoById: (id: string) => Promise<boolean>; getSongInfoById: (id: string) => Promise<boolean>;
/** 根据id切换歌曲 */ /** 根据id切换歌曲 */
switchSongById: (id: string) => Promise<boolean>; switchSongById: (id: string) => Promise<boolean>;
/** 切换歌曲 */ /**
* @description
* @diff -1: 1:
*/
switchSongByDiff: (diff: number) => void; switchSongByDiff: (diff: number) => void;
} }
@ -39,55 +39,71 @@ const useAuioState = create<AuioState>()(
devtools( devtools(
persist( persist(
(set, get) => { (set, get) => {
// 设置当前播放的音频
const setAudio = (id: string | null) => { const setAudio = (id: string | null) => {
console.log('触发setAudio');
const audio = get().playList.find((item) => item?.id === id) || null; const audio = get().playList.find((item) => item?.id === id) || null;
set({ audio }); set(
set({ playStatus: true }); produce((state) => {
state.audio = audio;
}),
);
}; };
// 根据 id 获取歌曲信息 /**
* @description id
* @mark 使`set({ audio: res.data });`
*/
const getSongInfoById = async (id: string) => { const getSongInfoById = async (id: string) => {
const res = await apiGetSongInfo(id); const res = await apiGetSongInfo(id);
if (res.code === 200) set({ audio: res.data }); if (res.code === 200) set({ audio: res.data });
return res.code === 200; return res.code === 200;
}; };
// 设置播放列表
const setPlayListId = (id: string) => {
set({ playListId: id });
};
// 切歌
const switchSongByDiff = (diff: number) => { const switchSongByDiff = (diff: number) => {
if (diff === 0) return;
const playList = get().playList; const playList = get().playList;
if (!playList) return; if (!playList) return;
const index = playList.findIndex((item) => item.id === get().audio?.id); const index = playList.findIndex((item) => item.id === get().audio?.id);
const nextIndex = index + diff; const nextIndex = index + diff; // 计算后的 index
if (nextIndex < 0) setAudio(playList[playList.length - 1].id);
if (nextIndex >= playList.length) setAudio(playList[0].id); // 判断 index 是否越界,计算音频 id
set({ audio: playList[nextIndex] }); let audioId = '';
setAudio(playList[nextIndex].id); if (nextIndex < 0) {
return; audioId = playList[playList.length - 1].id;
} else if (nextIndex >= playList.length) {
audioId = playList[0].id;
} else {
audioId = playList[nextIndex].id;
}
setAudio(audioId);
}; };
return { return {
playStatus: false, playStatus: false,
show: false, show: false,
audio: null, audio: null,
setAudio,
playList: [], playList: [],
setAudio,
setShow: (value) =>
set(
produce((state) => {
state.show = value;
}),
),
switchSongById: async (id: string) => { switchSongById: async (id: string) => {
const res = await getSongInfoById(id); const res = await getSongInfoById(id);
return res; return res;
}, },
playListId: '', playListId: '',
setPlayListId,
setPlayStatus: (value) => set({ playStatus: value }),
setShow: (value) => set({ show: value }),
setPlayList: (params) => { setPlayList: (params) => {
set({ playListId: params.id }); set(
set({ playList: params.list }); produce((state) => {
state.playListId = params.id;
state.playList = params.list;
}),
);
}, },
getSongInfoById, getSongInfoById,
switchSongByDiff, switchSongByDiff,

@ -53,7 +53,7 @@ const useUserStore = create<UserState>()(
thumbUpCount: 0, thumbUpCount: 0,
}; };
const setUserInfo = (value: UserInfo) => set(produce((state) => void (state.userInfo = value))); const setUserInfo = (value: UserInfo) => set(produce((state) => (state.userInfo = value)));
const getUserInfo = async () => { const getUserInfo = async () => {
const result = await apiGetMyUserInfo(); const result = await apiGetMyUserInfo();
@ -64,7 +64,7 @@ const useUserStore = create<UserState>()(
return { return {
userInfo: initialUserInfo, userInfo: initialUserInfo,
showLogin: false, showLogin: false,
setShowLogin: (value) => set({ showLogin: value }), setShowLogin: (value) => set(produce((state) => (state.showLogin = value))),
setUserInfo, setUserInfo,
userLogin: async (params) => { userLogin: async (params) => {
const result = await apiUserLogin(params); const result = await apiUserLogin(params);

Loading…
Cancel
Save