|
|
@ -2,8 +2,6 @@
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* 待优化:
|
|
|
|
* 待优化:
|
|
|
|
* 1. 拖动滚动条时音轨不要动,等拖动完毕后再动
|
|
|
|
* 1. 拖动滚动条时音轨不要动,等拖动完毕后再动
|
|
|
|
* 2. 切歌时,时间显示
|
|
|
|
|
|
|
|
* 3. 预加载
|
|
|
|
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
import { useState, useRef, useEffect } from 'react';
|
|
|
|
import { useState, useRef, useEffect } from 'react';
|
|
|
|
|
|
|
|
|
|
|
@ -14,99 +12,128 @@ 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 { audio, switchSongByDiff } = useAudioStore(
|
|
|
|
const { audioId, order, playQueue, switchSongByDiff, setOrder } = useAudioStore(
|
|
|
|
useShallow((state) => {
|
|
|
|
useShallow((state) => {
|
|
|
|
return {
|
|
|
|
return {
|
|
|
|
audio: state.audio,
|
|
|
|
audioId: state.audioId,
|
|
|
|
playList: state.playList,
|
|
|
|
order: state.order,
|
|
|
|
|
|
|
|
playQueue: state.playQueue,
|
|
|
|
|
|
|
|
setOrder: state.setOrder,
|
|
|
|
switchSongByDiff: state.switchSongByDiff,
|
|
|
|
switchSongByDiff: state.switchSongByDiff,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// const musicPlayers = useRef<HTMLAudioElement | undefined>(typeof Audio !== 'undefined' ? new Audio('') : undefined);
|
|
|
|
const audio: SongInfo = playQueue.find((item) => item.id === audioId) || playQueue[0];
|
|
|
|
const audioRef = useRef(new Audio(''));
|
|
|
|
|
|
|
|
|
|
|
|
const audioRef = useRef(new Audio(audio.src));
|
|
|
|
|
|
|
|
const isReady = useRef<boolean>(false); // 是否加载过组件
|
|
|
|
const [isPlaying, setIsPlaying] = useState(false); // 播放状态
|
|
|
|
const [isPlaying, setIsPlaying] = useState(false); // 播放状态
|
|
|
|
const [trackProgress, setTrackProgress] = useState<number>(0); // 音频进度(s)
|
|
|
|
const [trackProgress, setTrackProgress] = useState<number>(0); // 音频进度(s)
|
|
|
|
const isFirst = useRef<boolean>(false); // 第一次进入
|
|
|
|
const [duration, setDuration] = useState<number>(0); // 音频总时长(s)
|
|
|
|
const { duration } = audioRef.current; // 音频总时长(s)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 播放/暂停
|
|
|
|
// 修改播放顺序
|
|
|
|
const handlePlay = () => {
|
|
|
|
const handleChangeOrder = () => {
|
|
|
|
setIsPlaying(!isPlaying);
|
|
|
|
// 修改 icon & hover
|
|
|
|
|
|
|
|
const orderArr: PlayOrder[] = ['list_loop', 'random', 'single'];
|
|
|
|
|
|
|
|
const index = orderArr.indexOf(order);
|
|
|
|
|
|
|
|
setOrder(orderArr[(index + 1) % 3]);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 上一首
|
|
|
|
// 播放/暂停
|
|
|
|
const handlePlayPre = () => {
|
|
|
|
const handlePlay = () => {
|
|
|
|
switchSongByDiff(-1);
|
|
|
|
setIsPlaying((isPlaying) => !isPlaying);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 下一首
|
|
|
|
/**
|
|
|
|
const handlePlayNext = () => {
|
|
|
|
* @description 切换音乐
|
|
|
|
switchSongByDiff(1);
|
|
|
|
* @param direction -1: 上一首 1: 下一首
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
const handleSwitchAudio = (direction: number) => {
|
|
|
|
|
|
|
|
if (order === 'single') {
|
|
|
|
|
|
|
|
audioRef.current.currentTime = 0;
|
|
|
|
|
|
|
|
!isPlaying && setIsPlaying(true);
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
switchSongByDiff(direction);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 调整播放进度
|
|
|
|
// 调整播放进度
|
|
|
|
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);
|
|
|
|
|
|
|
|
setIsPlaying(true);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 监听音频进度
|
|
|
|
// 监听音频进度
|
|
|
|
const onTimeUpdate = () => {
|
|
|
|
const onTimeUpdate = () => {
|
|
|
|
if (audioRef.current.ended) handlePlayNext();
|
|
|
|
// 完成播放
|
|
|
|
setTrackProgress(audioRef.current.currentTime);
|
|
|
|
if (audioRef.current.ended) {
|
|
|
|
};
|
|
|
|
handleSwitchAudio(1);
|
|
|
|
|
|
|
|
audioRef.current.play();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 给 audio 绑定 timeupdate 监听
|
|
|
|
setTrackProgress(audioRef.current.currentTime);
|
|
|
|
const handleTimeUpdate = (status: boolean) => {
|
|
|
|
|
|
|
|
status
|
|
|
|
|
|
|
|
? audioRef.current.addEventListener('timeupdate', onTimeUpdate)
|
|
|
|
|
|
|
|
: audioRef.current.removeEventListener('timeupdate', onTimeUpdate);
|
|
|
|
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 播放/暂停
|
|
|
|
// 播放/暂停事件
|
|
|
|
useEffect(() => {
|
|
|
|
useEffect(() => {
|
|
|
|
isPlaying ? audioRef.current.play() : audioRef.current.pause();
|
|
|
|
isPlaying ? audioRef.current.play() : audioRef.current.pause();
|
|
|
|
}, [isPlaying]);
|
|
|
|
}, [isPlaying]);
|
|
|
|
|
|
|
|
|
|
|
|
// 切换歌曲
|
|
|
|
// 切换歌曲
|
|
|
|
useEffect(() => {
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
|
|
const handleLoadedMetadata = () => {
|
|
|
|
|
|
|
|
setDuration(audioRef.current.duration);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
audioRef.current.pause();
|
|
|
|
audioRef.current.pause();
|
|
|
|
if (!audio) return;
|
|
|
|
|
|
|
|
audioRef.current = new Audio(audio.src);
|
|
|
|
audioRef.current = new Audio(audio.src);
|
|
|
|
setTrackProgress(audioRef.current.currentTime);
|
|
|
|
audioRef.current.addEventListener('timeupdate', onTimeUpdate);
|
|
|
|
console.log('id变化', { audio, isFirst: isFirst.current });
|
|
|
|
|
|
|
|
if (isFirst.current) {
|
|
|
|
if (isReady.current) {
|
|
|
|
|
|
|
|
audioRef.current.addEventListener('loadedmetadata', handleLoadedMetadata); // 获取音频时长
|
|
|
|
audioRef.current.play();
|
|
|
|
audioRef.current.play();
|
|
|
|
setIsPlaying(true);
|
|
|
|
setIsPlaying(true);
|
|
|
|
handleTimeUpdate(true);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
isFirst.current = true;
|
|
|
|
isReady.current = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, [audio]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 卸载时的处理
|
|
|
|
return () => {
|
|
|
|
|
|
|
|
audioRef.current.removeEventListener('loadedmetadata', handleLoadedMetadata);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
}, [audioId]);
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
|
|
const handleKeyDown = (e: KeyboardEvent) => {
|
|
|
|
|
|
|
|
if (e.code === 'Space' || e.keyCode === 32) {
|
|
|
|
|
|
|
|
handlePlay();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
document.addEventListener('keydown', handleKeyDown); // 监听快捷键
|
|
|
|
|
|
|
|
setIsPlaying(false); // 禁止自动播放
|
|
|
|
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
return () => {
|
|
|
|
audioRef.current.pause();
|
|
|
|
audioRef.current.pause();
|
|
|
|
handleTimeUpdate(false);
|
|
|
|
audioRef.current.removeEventListener('timeupdate', onTimeUpdate);
|
|
|
|
|
|
|
|
document.removeEventListener('keydown', handleKeyDown);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}, []);
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
|
|
|
|
<PlayerControl
|
|
|
|
<PlayerControl
|
|
|
|
playStatus={isPlaying}
|
|
|
|
playStatus={isPlaying}
|
|
|
|
|
|
|
|
order={order}
|
|
|
|
audio={audio}
|
|
|
|
audio={audio}
|
|
|
|
duration={duration}
|
|
|
|
duration={duration}
|
|
|
|
onPlay={handlePlay}
|
|
|
|
onPlay={handlePlay}
|
|
|
|
onPrev={handlePlayPre}
|
|
|
|
onPrev={() => handleSwitchAudio(-1)}
|
|
|
|
onNext={handlePlayNext}
|
|
|
|
onNext={() => handleSwitchAudio(1)}
|
|
|
|
|
|
|
|
onOrder={handleChangeOrder}
|
|
|
|
className={className}
|
|
|
|
className={className}
|
|
|
|
trackProgress={trackProgress}
|
|
|
|
trackProgress={trackProgress}
|
|
|
|
onChangeProgress={handleChangeProgress}
|
|
|
|
onChangeProgress={handleChangeProgress}
|
|
|
|
/>
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|