update: styles

mack-mac
mackt 7 months ago
parent c0548118b8
commit 185db450ec

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 304 KiB

@ -34,7 +34,7 @@ export default function RootLayout({ children }: Readonly<{ children: React.Reac
<div>{children}</div>
<Footer />
<PlayerBar />
<ScrollTopButton className="fixed right-[50%] transform-translate-x-[600px] bottom-[480px] right-[124px]" />
<ScrollTopButton className="fixed right-[50%] transform-translate-x-[600px] bottom-[160px] right-[124px]" />
<Toaster />
</body>
</html>

@ -101,7 +101,7 @@ export default function Journal() {
</div>
{/* List */}
<SongCardList
className="w-full mt-11px mb-[56px]"
className="w-full mt-11px mb-[56px] ml-[-18px]"
songList={displayList}
listId="mylist"
collectType="playing"

@ -30,39 +30,39 @@ export default async function JournalDetail({ params: { journalId } }: { params:
{/* 封面 */}
<VolDetailCoverCard journalInfo={journalInfo} />
{/* 期刊号 & 标签 */}
<div className="flex flex-row justify-between w-full h-auto mt-[30px]">
<div className="flex flex-row items-center gap-[9px]">
<div className="text-[rgba(0,0,0,0.7)] text-[14px] leading-[19.6px]">{`VOL·${journalInfo?.journalNo}`}</div>
{journalInfo?.tags.length > 0 &&
journalInfo.tags.map((tag: string) => (
<Link
href="/"
key={tag}
className="block w-auto py-[3px] px-[10px] rounded-[15px] bg-[rgba(0,0,0,0.05)] text-[rgba(0,0,0,0.7)] text-[12px] leading-[12px]"
>
{tag}
</Link>
))}
</div>
{/* 收藏 */}
<CollectButton
showText
journalId={journalInfo.id}
active={journalInfo.haveCollect}
count={journalInfo.userCollectCount}
text="人收藏"
collectType="1"
iconPosition="right"
gap={9}
/>
<div className="flex flex-row items-center gap-[9px] mt-[30px]">
<div className="text-[rgba(0,0,0,0.7)] text-[14px] leading-[19.6px]">{`VOL·${journalInfo?.journalNo}`}</div>
{journalInfo?.tags.length > 0 &&
journalInfo.tags.map((tag: string) => (
<Link
href="/"
key={tag}
className="block w-auto py-[3px] px-[10px] rounded-[15px] bg-[rgba(0,0,0,0.05)] text-[rgba(0,0,0,0.7)] text-[12px] leading-[12px]"
>
{tag}
</Link>
))}
</div>
<div className="flex flex-row justify-between w-full h-auto">
{/* 标题 */}
<div>
<div className="w-autp text-[24px] leading-[33.6px] my-[9px] text-base font-bold text-overflow">
{journalInfo.title}
<div className="w-full">
<div className="flex flex-row justify-between items-center w-full h-auto">
<div className="w-autp text-[24px] leading-[33.6px] my-[9px] text-base font-bold text-overflow">
{journalInfo.title}
</div>
{/* 收藏 */}
<CollectButton
showText
journalId={journalInfo.id}
active={journalInfo.haveCollect}
count={journalInfo.userCollectCount}
text="人收藏"
collectType="1"
iconPosition="right"
gap={9}
/>
</div>
{/* 作者 & 时间 */}
<div className="flex flex-row item-center text-[rgba(0,0,0,0.4)] text-[12px] leading-[16.8px] gap-[12px]">
@ -78,7 +78,7 @@ export default async function JournalDetail({ params: { journalId } }: { params:
/>
{/* 卡片 */}
<SongCardList className="my-40px w-full" listId={journalInfo.id} songList={songList} />
<SongCardList className="my-40px w-full ml-[-18px]" listId={journalInfo.id} songList={songList} />
<Comment className="w-full" journalId={journalInfo.id} totalCommentReply={journalInfo.totalCommentReply} />
</div>

@ -1,5 +1,6 @@
'use client';
import { useEffect, useState } from 'react';
import { useEffect } from 'react';
import dynamic from 'next/dynamic';
import { usePathname } from 'next/navigation';
@ -12,53 +13,53 @@ import useAudioStore from '@/store/audio';
const PlayerBar = ({ className }: { className?: string }) => {
const pathname = usePathname();
const [showBar, setShowbar] = useState<boolean>(false);
const { audioId, showCard, setShowCard } = useAudioStore(
const { audioId, showCard, setShowCard, useShowPlayer } = useAudioStore(
useShallow((state) => {
return {
// show: state.show,
audioId: state.audioId,
useShowPlayer: state.useShowPlayer,
showCard: state.showCard,
setShowCard: state.setShowCard,
};
}),
);
let timer: NodeJS.Timeout;
function handleScroll() {
clearInterval(timer);
setShowbar(false);
timer = setTimeout(() => {
setShowbar(true);
}, 800);
}
const showPlayer = useShowPlayer();
useEffect(() => {
if (showCard) setShowCard(false);
}, [pathname]);
useEffect(() => {
window.addEventListener('scroll', handleScroll);
// let timer: NodeJS.Timeout;
// function handleScroll() {
// clearInterval(timer);
// setShowbar(false);
// timer = setTimeout(() => {
// setShowbar(true);
// }, 800);
// }
// useEffect(() => {
// window.addEventListener('scroll', handleScroll);
return () => {
window.removeEventListener('scroll', handleScroll);
clearInterval(timer);
};
}, []);
// return () => {
// window.removeEventListener('scroll', handleScroll);
// clearInterval(timer);
// };
// }, []);
return (
<div className={`fixed bottom-0 w-full h-auto z-10 ${className}`}>
{/* 播放器 */}
<div
className="fixed bottom-0 w-full h-[96px] bg-[#fff] bg-op-80 backdrop-blur-[15px] shadow-lg shadow-black-[0_0_10px] z-10 transition-bottom duration-600`"
style={{ bottom: (showBar && audioId) || showCard ? 0 : -130 }}
className="fixed bottom-0 w-full h-[80px] bg-[#fff] bg-op-80 backdrop-blur-[10px] shadow-lg shadow-black-[0_0_10px] z-10 transition-bottom duration-600`"
style={{ bottom: audioId || showCard ? 0 : -80 }}
>
<Player className="mx-auto" onSwitchShowCard={() => setShowCard(!showCard)} />
</div>
{/* 单曲卡片 */}
<PlayerCard show={showCard} />
<PlayerCard show={showCard} className="fixed left-0 bottom-0 w-100vw h-100vh" />
</div>
);
};

@ -29,7 +29,7 @@ export default function PlayerCard({ show, className }: Prop) {
const [lrc, setLrc] = useState<string>('');
const miniClass = 'translate-y-100vh'; // 缩小时的样式
const largeClass = 'translate-y-[-130px]'; // 放大后的样式
const largeClass = 'translate-y-0'; // 放大后的样式
const audioInfo = playQueue.find((item) => item.id === audioId);
@ -51,14 +51,13 @@ export default function PlayerCard({ show, className }: Prop) {
return (
<div
className={`fixed left-0 bottom-0 w-[100vw] h-[calc(100vh_-_130px)] bg-[#fff] transition-all duration-300 ${show ? largeClass : miniClass} ${className}`}
className={`w-100vw h-100vh bg-[#fff] transition-all duration-300 z-9 ${show ? largeClass : miniClass} ${className}`}
>
<Header />
<div className="flex justify-between w-[1200px] mx-auto mt-[68px]">
<div className="flex justify-between w-[1200px] mx-auto ">
{/* 单曲信息 */}
<div>
{/* 专辑封面 */}
<div className="mt-68px">
{/* 阴影 */}
<div className="relative w-[340px] h-[246px]">
<div
@ -69,6 +68,7 @@ export default function PlayerCard({ show, className }: Prop) {
borderRadius: '3px',
}}
/>
{/* 专辑封面 */}
{audioInfo?.pic && (
<Image
width={246}
@ -80,30 +80,37 @@ export default function PlayerCard({ show, className }: Prop) {
style={{ boxShadow: '27px 10px 30px -12px rgba(0, 0, 0, 0.25)' }}
/>
)}
{/* CD */}
<div
className={`absolute right-0 top-[13px] w-[220px] h-[220px] bg-[url(/img/audio-player/CD.png)] z-2 ${styles.CD_animation}`}
className={`absolute right-0 top-[13px] w-[220px] h-[220px] bg-[url(/img/audio-player/CD.png)] bg-cover bg-no-repeat z-2 ${styles.CD_animation}`}
/>
</div>
{/* 歌曲名 */}
<p className="mt-[44px] text-[20px] leading-[28px] text-base">{audioInfo?.title ?? ''}</p>
<p className="w-[340px] mt-[44px] text-[20px] leading-[28px] text-base text-overflow">
{audioInfo?.title ?? ''}
</p>
{/* 歌手/专辑 */}
{audioInfo?.artist && audioInfo?.album && (
<p className="text-[13px] leading-[18px] mt-[2px] text-[rgba(0,0,0,0.7)]">{`${audioInfo.artist}/${audioInfo.album}`}</p>
<p className="w-[340px] text-[13px] leading-[18px] mt-[2px] text-[rgba(0,0,0,0.7)] text-overflow">{`${audioInfo.artist}/${audioInfo.album}`}</p>
)}
{/* 歌词 */}
{lrc ? (
<Lrc lrc={lrc} className="mt-[30px] w-[340px] h-[267px]" />
) : (
<div className="mt-[30px] w-[340px] h-[267px] text-[14px] text-[rgba(0,0,0,0.7)] text-center leading-[267px]">
</div>
)}
<div className={`mt-[30px] w-[340px]`} style={{ height: 'calc(100vh - 516px - 209px)' }}>
{lrc ? (
<Lrc lrc={lrc} className="h-full" />
) : (
<div className={`h-full text-[14px] text-[rgba(0,0,0,0.7)] text-center leading-[267px]`}></div>
)}
</div>
</div>
{/* 播放列表 */}
<div className="w-[712px]">
<div className="w-728px mt-49px">
<button></button>
<div className={`h-[620px] mt-[15px] overflow-y-auto ${styles.playQueue}`}>
<SongCardList className="w-full mt-11px" songList={playQueue} listId="single" />
<div
className={`w-full mt-[15px] ml-[-18px] pr-14px !overflow-x-visible overflow-y-auto ${styles.playQueue}`}
style={{ height: 'calc(100vh - 168px - 140px - 80px)' }}
>
<SongCardList className="w-[712px] mt-11px" songList={playQueue} listId="single" />
</div>
</div>
</div>

@ -101,15 +101,21 @@
.playQueue::-webkit-scrollbar-thumb {
/* 滚动条圆角 */
border-radius: 3px;
background: #d9d9d9; /* 滚动条滑块颜色 */
/* 滚动条滑块颜色 */
/* background: #d9d9d9; */
background: rgba(0, 0, 0, 0); /* 滚动条滑块颜色 */
}
.playQueue:hover::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.05); /* 滚动条滑块悬停时颜色 */
}
.playQueue::-webkit-scrollbar-thumb:hover {
background: #555; /* 滚动条滑块悬停时颜色 */
background: rgba(0, 0, 0, 0.3); /* 滚动条滑块悬停时颜色 */
}
.CD_animation {
animation: spin 1.6s linear infinite;
animation: spin 4.5s linear infinite;
}
@keyframes spin {

@ -4,7 +4,7 @@
import { useEffect, useState } from 'react';
import styles from './index.module.css';
import styles from './thumb.module.css';
interface Props {
/** 是否显示文案 */
@ -68,7 +68,7 @@ export default function ThumbupButton({
{/* 文案 */}
{!!(showText && currentCount) && (
<p
className={` pb-2px group-hover:text-brand ${haveThumb ? 'text-brand' : 'text-[rgba(0,0,0,0.4)]'} text-13px leading-18.2px ${textClassName} ${styles.text} transition-colors`}
className={` pb-2px ${haveThumb ? 'text-brand' : 'text-[rgba(0,0,0,0.4)] group-hover:text-base'} text-13px leading-18.2px ${textClassName} ${styles.text} transition-colors-500`}
>
{`${currentCount || ''}${text || ''}`}
</p>
@ -81,11 +81,11 @@ export default function ThumbupButton({
function Icon({ haveThumb }: { haveThumb: boolean }) {
return (
<div className={`${styles['heart-icon']}`}>
<div className={`${styles['thumb-icon']}`}>
<input type="checkbox" readOnly className={styles.checkbox} checked={haveThumb} />
<svg
className={styles['heart-svg']}
className={styles['thumb-svg']}
width="24"
height="24"
viewBox="0 0 24 24"
@ -93,10 +93,8 @@ function Icon({ haveThumb }: { haveThumb: boolean }) {
xmlns="http://www.w3.org/2000/svg"
>
<path
className={`${styles['heart-path']} group-hover:stroke-[#c43737] transition-stroke`}
className={`${styles['thumb-path']} group-hover:stroke-[#000] transition-stroke`}
d="M8.74229 18.4016H6.82182C6.48226 18.4016 6.15661 18.2667 5.9165 18.0266C5.67639 17.7865 5.5415 17.4609 5.5415 17.1213V12.6402C5.5415 12.3006 5.67639 11.975 5.9165 11.7349C6.15661 11.4948 6.48226 11.3599 6.82182 11.3599H8.74229M13.2234 10.0796V7.51893C13.2234 7.00958 13.0211 6.5211 12.6609 6.16094C12.3007 5.80079 11.8123 5.59845 11.3029 5.59845L8.74229 11.3599V18.4016H15.9633C16.272 18.4051 16.5717 18.2969 16.8069 18.0969C17.0422 17.8969 17.1973 17.6187 17.2436 17.3134L18.127 11.5519C18.1549 11.3684 18.1425 11.1811 18.0907 11.0028C18.039 10.8246 17.9491 10.6597 17.8273 10.5197C17.7055 10.3796 17.5548 10.2677 17.3854 10.1917C17.2161 10.1157 17.0323 10.0775 16.8467 10.0796H13.2234Z"
stroke="black"
strokeOpacity="0.7"
strokeWidth="1.2"
strokeLinecap="round"
strokeLinejoin="round"

@ -50,6 +50,19 @@
animation: run 0.75s 0.1s forwards linear;
}
.hover_mask:hover::after {
display: block;
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.05);
border-radius: 2px;
z-index: 2;
}
/* 填充 */
@keyframes run {
0% {
@ -82,3 +95,45 @@
transform: scale(1.25);
}
}
.thumb-icon {
width: var(--size);
position: relative;
--gray-color: rgba(0, 0, 0, 0.4);
--no-color: rgba(244, 244, 244, 0);
--skin-color: #c43737;
--size: 24px;
--path-dasharray: 120;
}
.thumb-svg {
width: 100%;
position: relative;
z-index: 9;
}
.thumb-path {
fill: var(--no-color);
stroke: var(--gray-color);
stroke-dasharray: var(--path-dasharray);
stroke-width: 1.5px;
stroke-dashoffset: 0;
stroke-linecap: round;
}
.checkbox[type='checkbox'] {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 99;
opacity: 0;
}
.checkbox[type='checkbox']:hover + svg .thumb-path {
stroke: #000;
transition: stroke 0.2s ease-in-out;
}

@ -0,0 +1,82 @@
.thumb-icon {
width: var(--size);
position: relative;
--gray-color: rgba(0, 0, 0, 0.4);
--no-color: rgba(244, 244, 244, 0);
--brand-color: #c43737;
--size: 24px;
--path-dasharray: 120;
}
.thumb-svg {
width: 100%;
position: relative;
z-index: 9;
}
.thumb-path {
fill: var(--no-color);
stroke: var(--gray-color);
stroke-dasharray: var(--path-dasharray);
stroke-width: 1.5px;
stroke-dashoffset: 0;
stroke-linecap: round;
}
.checkbox[type='checkbox'] {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 99;
opacity: 0;
}
.checkbox[type='checkbox']:hover + svg .thumb-path {
stroke: #000;
transition: stroke 0.2s ease-in-out;
}
/* 抖动 */
.checkbox[type='checkbox']:checked + svg {
animation: touch 0.7s forwards ease-in;
}
/* 填充 */
.checkbox[type='checkbox']:checked + svg .thumb-path {
animation: run 0.75s 0.1s forwards linear;
}
@keyframes run {
0% {
stroke: var(--brand-color);
stroke-dashoffset: var(--path-dasharray);
}
80% {
stroke-dashoffset: 0;
}
100% {
stroke: var(--brand-color);
stroke-dashoffset: 0;
}
}
/* 抖动 */
@keyframes touch {
0%,
50%,
100% {
transform: scale(1);
}
25% {
transform: scale(0.75);
}
75% {
transform: scale(1.25);
}
}

@ -42,7 +42,7 @@ export default function QRCodeDialog({
<p className="text-[#000] text-[24px] leading-[34px] font-bold ">{headerText}</p>
<p className="mt-[2px] text-[#000] text-opacity-70 text-[13px] leading-[18px] ">{`最新版V${version}`}</p>
<div className="w-[200px] h-[200px] mt-[24px] bg-[#fff] rounded-[24px] overflow-hidden">
<div className="w-[200px] h-[200px] mt-[24px] bg-[#fff] rounded-[6px] overflow-hidden">
{!!url && <QRCode text={url} className="w-full h-full" />}
</div>

@ -12,7 +12,7 @@ interface Props extends SongInfo {
onPlay: (id: string) => void;
}
export default function JournalItem({
export default function SongCard({
playState,
id,
title,
@ -26,17 +26,30 @@ export default function JournalItem({
}: Props) {
return (
<div
className="flex flex-row items-center justify-between w-[calc(100%_+_18px)] h-[72px] ml-[-18px] my-[3px] py-[12px] px-[18px] rounded-[3px] hover:bg-[#f2f3f7] group cursor-pointer"
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"
onClick={() => onPlay(id)}
>
{/* 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 />
<Image
width={48}
height={48}
src={pic}
alt={title}
className="w-[48px] h-[48px] rounded-[3px] 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>
<div className={`flex flex-col justify-between ml-[15px]`}>
<div className={`text-[15px] leading-[21px] text-base group-hover:text-brand ${showEq && 'text-brand'}`}>
{title}
</div>
<div
className={`text-[13px] leading-[18.2px] text-[rgba(0,0,0,0.7)] group-hover:text-brand ${showEq && 'text-brand'}`}
>
{artist}
</div>
</div>
</div>
@ -44,8 +57,10 @@ export default function JournalItem({
<div className="flex flex-row items-center">
{/* 均衡器效果 */}
{showEq && <IconEqualizer 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 && (
<CollectButton

@ -19,7 +19,7 @@ interface Props {
collectType?: 'always' | 'playing';
}
export default function JournalItem({ songList, className, collectType = 'always' }: Props) {
export default function SongCardList({ songList, className, collectType = 'always' }: Props) {
const { playState, audioId, setPlayState, setAudioId, setPlayList } = useAudioStore(
useShallow((state) => ({
playState: state.playState,

@ -8,8 +8,6 @@ interface AuioState {
playState: boolean;
/** 播放顺序 list_loop: 列表循环 random: 随机 single: 单曲 */
order: PlayOrder;
/** 播放器显示状态 true: 显示 false: 隐藏 */
show: boolean;
/** 单曲卡牌展示 true: 显示 false: 隐藏 */
showCard: boolean;
/** 当前音频id */
@ -18,6 +16,7 @@ interface AuioState {
playList: SongInfo[];
/** 播放队列 */
playQueue: SongInfo[];
useShowPlayer: () => boolean;
setPlayState: (state: boolean) => void;
setOrder: (order: PlayOrder) => void;
setAudioId: (id: string) => void;
@ -105,6 +104,10 @@ const useAuioState = create<AuioState>()(
);
};
const useShowPlayer = () => {
return !!(get().audioId || get().showCard);
};
return {
audioId: '',
order: 'list_loop',
@ -113,6 +116,7 @@ const useAuioState = create<AuioState>()(
showCard: false,
playList: [],
playQueue: [],
useShowPlayer,
setAudioId,
setOrder,
setPlayState: (value) =>

Loading…
Cancel
Save