update(AudioPlayer): 音频播放逻辑

mack-mac
mackt 7 months ago
parent 5846c05e08
commit 5c68eb8322

@ -10,6 +10,7 @@ import styles from '../index.module.css';
import { SongCardList, Pagination } from '@/components';
import { apiGetSongCollect } from '@/services';
import useAudioStore from '@/store/audio';
import useUserStore from '@/store/user';
export default function Journal() {
@ -19,6 +20,13 @@ export default function Journal() {
})),
);
const { setPlayList, setAudioId } = useAudioStore(
useShallow((state) => ({
setAudioId: state.setAudioId,
setPlayList: state.setPlayList,
})),
);
const [type, setType] = useState<'single' | 'journal'>('single'); // 收藏类型
const [total, setTotal] = useState<number>(0);
const [pageNum, setPageNum] = useState<number>(1);
@ -36,6 +44,13 @@ export default function Journal() {
setType(type);
};
// 播放
const handlePlay = (id: string) => {
if (!id) return;
setPlayList(list);
setAudioId(id);
};
const getList = async () => {
setLoading(true);
const result = await apiGetSongCollect({ userId: userInfo?.id, pageNum: -1, pageSize: -1 });
@ -77,11 +92,20 @@ export default function Journal() {
</li>
))}
</ul>
<div className="w-[105px] h-[36px] mt-[50px] pl-[45px] text-base text-[15px] leading-[36px] bg-[url(/img/icon/play-ellipse.svg)] bg-no-repeat bg-left hover:text-brand cursor-pointer">
{/* 播放按钮 */}
<div
className="w-[105px] h-[36px] mt-[50px] pl-[45px] text-base text-[15px] leading-[36px] bg-[url(/img/icon/play-ellipse.svg)] bg-no-repeat bg-left hover:text-brand cursor-pointer"
onClick={() => handlePlay(list?.[0]?.id)}
>
</div>
{/* List */}
<SongCardList className="w-full mt-11px mb-[56px]" songList={displayList} listId="mylist" />
<SongCardList
className="w-full mt-11px mb-[56px]"
songList={displayList}
listId="mylist"
collectType="playing"
/>
{!!total && (
<Pagination

@ -18,13 +18,15 @@ export default function AudioPlayer({
className?: string;
onSwitchShowCard: () => void;
}) {
const { audioId, order, playQueue, showCard, switchSongByDiff, setOrder } = useAudioStore(
const { playState, audioId, order, playQueue, showCard, setPlayState, switchSongByDiff, setOrder } = useAudioStore(
useShallow((state) => {
return {
playState: state.playState,
audioId: state.audioId,
order: state.order,
playQueue: state.playQueue,
showCard: state.showCard,
setPlayState: state.setPlayState,
setOrder: state.setOrder,
switchSongByDiff: state.switchSongByDiff,
};
@ -35,7 +37,6 @@ export default function AudioPlayer({
const audioRef = useRef(new Audio(audio?.src || ''));
const isReady = useRef<boolean>(false); // 是否加载过组件
const [isPlaying, setIsPlaying] = useState(false); // 播放状态
const [trackProgress, setTrackProgress] = useState<number>(0); // 音频进度(s)
const [duration, setDuration] = useState<number>(0); // 音频总时长(s)
@ -49,7 +50,7 @@ export default function AudioPlayer({
// 播放/暂停
const handlePlay = () => {
setIsPlaying((isPlaying) => !isPlaying);
setPlayState(!playState);
};
/**
@ -59,7 +60,7 @@ export default function AudioPlayer({
const handleSwitchAudio = (direction: number) => {
if (order === 'single') {
audioRef.current.currentTime = 0;
!isPlaying && setIsPlaying(true);
!playState && setPlayState(true);
return;
}
switchSongByDiff(direction);
@ -69,7 +70,7 @@ export default function AudioPlayer({
const handleChangeProgress = (value: string) => {
audioRef.current.currentTime = Number(value);
setTrackProgress(audioRef.current.currentTime);
setIsPlaying(true);
setPlayState(true);
};
// 监听音频进度
@ -85,8 +86,8 @@ export default function AudioPlayer({
// 播放/暂停事件
useEffect(() => {
isPlaying ? audioRef.current.play() : audioRef.current.pause();
}, [isPlaying]);
playState ? audioRef.current.play() : audioRef.current.pause();
}, [playState]);
// 切换歌曲
useEffect(() => {
@ -101,7 +102,7 @@ export default function AudioPlayer({
if (isReady.current) {
audioRef.current.addEventListener('loadedmetadata', handleLoadedMetadata); // 获取音频时长
audioRef.current.play();
setIsPlaying(true);
setPlayState(true);
} else {
isReady.current = true;
}
@ -119,7 +120,7 @@ export default function AudioPlayer({
};
document.addEventListener('keydown', handleKeyDown); // 监听快捷键
setIsPlaying(false); // 禁止自动播放
setPlayState(false); // 禁止自动播放
return () => {
audioRef.current.pause();
@ -130,7 +131,7 @@ export default function AudioPlayer({
return (
<PlayerControl
playStatus={isPlaying}
playStatus={playState}
order={order}
audio={audio}
duration={duration}

@ -0,0 +1,13 @@
// 均衡器效果
import styles from './index.module.css';
export default function Equalizer({ active }: { active: boolean }) {
return (
<div className={`${styles.equalizer} ${!active && styles.paused}`}>
<span></span>
<span></span>
<span></span>
</div>
);
}

@ -0,0 +1,73 @@
.equalizer {
width: 24px;
height: 24px;
padding: 6px;
box-sizing: border-box;
display: flex;
justify-content: space-between;
}
.equalizer span {
width: 2px;
border-radius: 4px;
background: #c43737ff;
animation: up-and-down 2s linear infinite;
}
.equalizer span:nth-child(1) {
animation-delay: 0.4s;
}
.equalizer span:nth-child(2) {
}
.equalizer span:nth-child(3) {
animation-delay: 0.7s;
}
.equalizer.paused span {
animation-play-state: paused;
}
@keyframes up-and-down {
0%,
100% {
clip-path: inset(27% 0 0 0);
}
10% {
clip-path: inset(17% 0 0 0);
}
20% {
clip-path: inset(55% 0 0 0);
}
30% {
clip-path: inset(30% 0 0 0);
}
40% {
clip-path: inset(13% 0 0 0);
}
50% {
clip-path: inset(38% 0 0 0);
}
60% {
clip-path: inset(80% 0 0 0);
}
70% {
clip-path: inset(21% 0 0 0);
}
80% {
clip-path: inset(0% 0 0 0);
}
90% {
clip-path: inset(36% 0 0 0);
}
}

@ -1,43 +0,0 @@
/**
*
*/
'use client';
import { useShallow } from 'zustand/react/shallow';
import { SongCard } from '@/components';
import useAudioStore from '@/store/audio';
interface Props {
listId: string;
songList: SongInfo[];
className?: string;
}
export default function JournalItem({ listId, songList, className }: Props) {
const { setPlayList, setAudioId, playListId } = useAudioStore(
useShallow((state) => ({
setAudioId: state.setAudioId,
setPlayList: state.setPlayList,
playListId: state.playListId,
})),
);
const handlePlayList = (id: string) => {
// 正在播放其他歌单
if (playListId !== listId) {
setPlayList({ id: listId, list: songList });
}
// 正在播放当前歌单
setAudioId(id);
};
return (
<div className={`${className}`}>
{songList.map((song: SongInfo) => (
<SongCard key={song.id} {...song} onPlay={(audioId: string) => handlePlayList(audioId)} />
))}
</div>
);
}

@ -1,16 +1,29 @@
import Image from 'next/image';
import { ButtonCollect } from '@/components';
import { ButtonCollect, Equalizer } from '@/components';
interface Props extends SongInfo {
/** 播放状态 */
playState: boolean;
/** 展示收藏按钮 */
showCollect: boolean;
/** 显示均衡器效果 */
showEq: boolean;
onPlay: (id: string) => void;
}
export default function JournalItem({
playState,
id,
title,
pic,
artist,
haveCollect,
duration,
showEq,
showCollect,
onPlay,
}: SongInfo & { onPlay: (id: string) => void }) {
}: Props) {
return (
<div
className="flex flex-row items-center justify-between w-full h-[72px] my-[3px] py-[12px] px-[18px] rounded-[3px] hover:bg-[#f2f3f7] group cursor-pointer"
@ -18,7 +31,9 @@ export default function JournalItem({
>
{/* left */}
<div className="flex flex-row items-center">
{/* 专辑封面 */}
<Image width={48} height={48} src={pic} alt={title} className="w-[48px] h-[48px] overflow-hidden" unoptimized />
{/* 歌曲名称/歌手 */}
<div className="flex flex-col justify-between ml-[15px]">
<div className="text-[15px] leading-[21px] text-base group-hover:text-brand">{title}</div>
<div className="text-[13px] leading-[18.2px] text-[rgba(0,0,0,0.7)] group-hover:text-brand">{artist}</div>
@ -27,22 +42,19 @@ export default function JournalItem({
{/* right */}
<div className="flex flex-row items-center">
<Image
width={24}
height={24}
src={'/img/icon/audio-playing.svg'}
alt={title}
className="w-[24px] h-[24px] overflow-hidden"
/>
{/* 均衡器效果 */}
{showEq && <Equalizer active={playState} />}
{/* 音频时长 */}
<p className="ml-[30px] mr-[13px] text-[12px] leading-[16.8px] text-[rgba(0,0,0,0.4)]">{duration || '00:00'}</p>
{/* 收藏按钮 */}
{showCollect && (
<ButtonCollect
journalId={id}
active={haveCollect}
collectType="0"
textClassName="w-[42px] ml-[6px] mr-[24px] text-[14px] leading-[16px]"
/>
)}
</div>
</div>
);

@ -0,0 +1,58 @@
/**
*
*/
'use client';
import { useShallow } from 'zustand/react/shallow';
import { SongCard } from '@/components';
import useAudioStore from '@/store/audio';
interface Props {
listId: string;
songList: SongInfo[];
className?: string;
/**
* always:
* playing:
*/
collectType?: 'always' | 'playing';
}
export default function JournalItem({ songList, className, collectType = 'always' }: Props) {
const { playState, audioId, setPlayState, setAudioId, setPlayList } = useAudioStore(
useShallow((state) => ({
playState: state.playState,
audioId: state.audioId,
setPlayState: state.setPlayState,
setAudioId: state.setAudioId,
setPlayList: state.setPlayList,
})),
);
const handlePlayList = (id: string) => {
if (!id) return;
if (id === audioId) {
setPlayState(!playState);
return;
}
// 设置播放列表、音频id
setPlayList(songList);
setAudioId(id);
};
return (
<div className={`${className}`}>
{songList.map((item: SongInfo) => (
<SongCard
key={item.id}
{...item}
playState={playState}
showEq={item.id === audioId}
onPlay={(audioId: string) => handlePlayList(audioId)}
showCollect={collectType === 'always' || (collectType === 'playing' && item.id === audioId)}
/>
))}
</div>
);
}

@ -2,6 +2,9 @@ export { default as Logo } from './Logo';
export { default as Header } from './Header/index';
export { default as Footer } from './Footer';
// Icon
export { default as Equalizer } from './Icon/Equalizer';
// Home
export { default as Category } from './Category';
export { default as ContributorCard } from './ContributorCard';
@ -28,9 +31,9 @@ export { default as ButtonCollect } from './ButtonCollect';
export { default as PlayerBar } from './AudioPlayer/PlayerBar';
export { default as AudioPlayer } from './AudioPlayer/Player';
// Song
export { default as SongCard } from './Song/SongCard';
export { default as SongCardList } from './Song/SongCardList';
// SongCard
export { default as SongCard } from './SongCard/SongCard';
export { default as SongCardList } from './SongCard/SongCardList';
// Comment
export { default as Comment } from './Comment/Comment';
@ -39,4 +42,4 @@ export { default as CommentForm } from './Comment/CommentForm';
export { default as CommentItem } from './Comment/CommentItem';
export { default as CommentList } from './Comment/CommentList';
export { default as PlayerButton } from './AudioPlayer/PlayerButton';
export { default as Pagination } from './Pagination/index';
export { default as Pagination } from './Pagination';

@ -4,15 +4,13 @@ import { create } from 'zustand';
import { devtools, persist, createJSONStorage } from 'zustand/middleware';
interface AuioState {
/** 播放状态 */
playStatus: boolean;
/** 播放列表id */
playListId: string;
/** 播放顺序 */
/** 播放状态 true: 播放 false: 暂停 */
playState: boolean;
/** 播放顺序 list_loop: 列表循环 random: 随机 single: 单曲 */
order: PlayOrder;
/** 播放器显示状态 */
/** 播放器显示状态 true: 显示 false: 隐藏 */
show: boolean;
/** 单曲卡牌展示 */
/** 单曲卡牌展示 true: 显示 false: 隐藏 */
showCard: boolean;
/** 当前音频id */
audioId: string;
@ -20,6 +18,7 @@ interface AuioState {
playList: SongInfo[];
/** 播放队列 */
playQueue: SongInfo[];
setPlayState: (state: boolean) => void;
setOrder: (order: PlayOrder) => void;
setAudioId: (id: string) => void;
// 显示/隐藏播放器
@ -27,7 +26,7 @@ interface AuioState {
// 显示/隐藏单曲卡片
setShowCard: (value: boolean) => void;
// 设置播放列表
setPlayList: (params: { id: string; list: SongInfo[] }) => void;
setPlayList: (list: SongInfo[]) => void;
// 切换歌曲 -1: 上一首 1: 下一首
switchSongByDiff: (diff: number) => void;
}
@ -37,8 +36,20 @@ const useAuioState = create<AuioState>()(
persist(
(set, get) => {
const setAudioId = (id: string) => {
if (id === get().audioId) return; // 当前歌曲
if (get().playQueue.findIndex((item) => item.id === id) < 0) return; // 不在歌单内
if (id === get().audioId) {
// 传入当前歌曲id 切换播放/暂停状态
// set(
// produce((state) => {
// state.playState = !get().playState;
// }),
// );
return;
}
// 不在歌单内
if (get().playQueue.findIndex((item) => item.id === id) < 0) {
return;
}
set(
produce((state) => {
@ -97,14 +108,19 @@ const useAuioState = create<AuioState>()(
return {
audioId: '',
order: 'list_loop',
playStatus: false,
playState: false,
show: false,
showCard: false,
playListId: '',
playList: [],
playQueue: [],
setAudioId,
setOrder,
setPlayState: (value) =>
set(
produce((state) => {
state.playState = value;
}),
),
setShow: (value) =>
set(
produce((state) => {
@ -117,12 +133,12 @@ const useAuioState = create<AuioState>()(
state.showCard = value;
}),
),
setPlayList: (params) => {
setPlayList: (list) => {
set(
produce((state) => {
state.playListId = params.id;
state.playList = params.list;
state.playQueue = params.list;
state.playList = list;
state.order = 'list_loop';
state.playQueue = list;
}),
);
},

Loading…
Cancel
Save