After Width: | Height: | Size: 313 B |
After Width: | Height: | Size: 313 B |
After Width: | Height: | Size: 917 B |
After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 264 B After Width: | Height: | Size: 272 B |
Before Width: | Height: | Size: 303 B After Width: | Height: | Size: 372 B |
Before Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 341 B |
After Width: | Height: | Size: 1.8 KiB |
@ -0,0 +1,114 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import Image from 'next/image';
|
||||||
|
|
||||||
|
import { secondToDate } from '@/utils/timeFormat';
|
||||||
|
|
||||||
|
import { AutoScrollContainer } from '@/components';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
audio: SongInfo | null;
|
||||||
|
playStatus: boolean;
|
||||||
|
onPlay: () => void;
|
||||||
|
onPrev: () => void;
|
||||||
|
onNext: () => void;
|
||||||
|
onChangeProgress: (value: string) => void;
|
||||||
|
trackProgress: number;
|
||||||
|
duration: number;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AudioPlayer({
|
||||||
|
audio,
|
||||||
|
playStatus,
|
||||||
|
onPlay,
|
||||||
|
onPrev,
|
||||||
|
onNext,
|
||||||
|
onChangeProgress,
|
||||||
|
trackProgress,
|
||||||
|
duration,
|
||||||
|
className,
|
||||||
|
}: Props) {
|
||||||
|
const currentPercentage = duration ? `${(trackProgress / duration) * 100}%` : '0%';
|
||||||
|
const trackStyling: string = `
|
||||||
|
-webkit-gradient(linear, 0% 0%, 100% 0%, color-stop(${currentPercentage}, rgb(24,24,24)), color-stop(${currentPercentage}, rgba(0,0,0,0.1)))
|
||||||
|
`; // 进度条样式
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`flex flex-row w-[1200px] pt-[29px] ${className}`}>
|
||||||
|
{/* 专辑封面 */}
|
||||||
|
<div className="w-[72px] h-[72px] rounded-[3px] bg-[#000]">
|
||||||
|
{audio?.pic && (
|
||||||
|
<Image src={audio?.pic} alt="music" width={72} height={72} unoptimized className="w-full h-full" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* title & author */}
|
||||||
|
<div className="h-full ml-[27px] mr-[44px] py-[14px]">
|
||||||
|
<AutoScrollContainer auto hover width="140px" speed={50}>
|
||||||
|
<div className="w-auto h-auto">
|
||||||
|
<p className="text-[17px] leading-[23.8px] text-[rgba(0,0,0,0.95)]">{audio?.title}</p>
|
||||||
|
<p className="text-[13px] leading-[18.2px] text-[rgba(0,0,0,0.7)]">{`${audio?.artist}/${audio?.album}`}</p>
|
||||||
|
</div>
|
||||||
|
</AutoScrollContainer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* progress bar */}
|
||||||
|
<div className="h-full mt-[29px]">
|
||||||
|
{/* bar */}
|
||||||
|
<div className="h-full cursor-pointer">
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
value={trackProgress}
|
||||||
|
step="1"
|
||||||
|
min="0"
|
||||||
|
max={duration ? duration : `${duration}`}
|
||||||
|
className="w-[600px] h-[3px]"
|
||||||
|
onChange={(e) => onChangeProgress(e.target.value)}
|
||||||
|
style={{ background: trackStyling }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/* time */}
|
||||||
|
<p className="texe-[12px] leading[16.8px]">
|
||||||
|
<span>{`${secondToDate(trackProgress)}`}</span>
|
||||||
|
<span className="text-[rgba(0,0,0,0.4)]">{` /${secondToDate(duration)}`}</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* control */}
|
||||||
|
<div className="flex flex-row items-center">
|
||||||
|
<button>
|
||||||
|
{/* 随机播放 */}
|
||||||
|
<Image
|
||||||
|
src={'/img/audio-player/random.svg'}
|
||||||
|
alt="random"
|
||||||
|
width={28}
|
||||||
|
height={28}
|
||||||
|
className="ml-[55px] mr-[60px]"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
{/* 上一首 */}
|
||||||
|
<button onClick={onPrev}>
|
||||||
|
<Image src={'/img/audio-player/next.svg'} alt="pre" width={28} height={28} className="rotate-180" />
|
||||||
|
</button>
|
||||||
|
{/* 播放/暂停 */}
|
||||||
|
<button
|
||||||
|
className="flex justify-center items-center w-[54px] h-[54px] mx-[35px] rounded-[50%] bg-[rgba(180,67,67,1)] overflow-hidden"
|
||||||
|
onClick={onPlay}
|
||||||
|
>
|
||||||
|
{/* <Image src={'/img/audio-player/play.svg'} alt="play" width={28} height={28} /> */}
|
||||||
|
<Image
|
||||||
|
src={playStatus ? '/img/audio-player/pause.svg' : '/img/audio-player/play.svg'}
|
||||||
|
alt="pause"
|
||||||
|
width={28}
|
||||||
|
height={28}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
{/* 下一首 */}
|
||||||
|
<button onClick={onNext}>
|
||||||
|
<Image src={'/img/audio-player/next.svg'} alt="next" width={28} height={28} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
export const secondToDate: (v: number) => string = (second) => {
|
||||||
|
const hourNum = Math.floor(second / 3600);
|
||||||
|
const minuteNum = Math.floor((second / 60) % 60);
|
||||||
|
const secondNum = Math.floor(second % 60);
|
||||||
|
|
||||||
|
const h = hourNum ? `${hourNum}:` : '';
|
||||||
|
const m = minuteNum ? `${unitAddZero(minuteNum)}:` : `00:`;
|
||||||
|
const s = unitAddZero(secondNum);
|
||||||
|
|
||||||
|
return `${h}${m}${s}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 格式化个位数
|
||||||
|
const unitAddZero: (v: number) => string = (v) => {
|
||||||
|
return v < 10 ? `0${v}` : `${v}`;
|
||||||
|
};
|