优化图文页
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
||||
Minimize2,
|
||||
Pause,
|
||||
Play,
|
||||
Repeat,
|
||||
Repeat1,
|
||||
RotateCcw,
|
||||
RotateCw,
|
||||
@@ -89,25 +90,70 @@ export function MediaControls({
|
||||
onDownload,
|
||||
onToggleFullscreen,
|
||||
}: MediaControlsProps) {
|
||||
const normalizedLoopMode =
|
||||
isVideo && loopMode === "single" ? "loop" : loopMode;
|
||||
const loopLabel = isVideo
|
||||
? normalizedLoopMode === "loop"
|
||||
? "循环播放"
|
||||
: "顺序播放"
|
||||
: loopMode === "single"
|
||||
? "单页循环"
|
||||
: loopMode === "loop"
|
||||
? "图文循环"
|
||||
: "顺序播放";
|
||||
const handleLoopModeToggle = () => {
|
||||
if (isVideo) {
|
||||
onLoopModeChange(normalizedLoopMode === "loop" ? "sequential" : "loop");
|
||||
return;
|
||||
}
|
||||
|
||||
onLoopModeChange(
|
||||
loopMode === "loop"
|
||||
? "single"
|
||||
: loopMode === "single"
|
||||
? "sequential"
|
||||
: "loop",
|
||||
);
|
||||
};
|
||||
const renderLoopIcon = () => {
|
||||
if (!isVideo && loopMode === "single") return <Repeat1 size={18} />;
|
||||
if (normalizedLoopMode === "loop") return <Repeat size={18} />;
|
||||
return <ArrowDownUp size={18} />;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="absolute left-0 right-0 bottom-0 px-3 pb-4 pt-2 bg-gradient-to-b from-black/0 via-black/45 to-black/65 flex flex-col gap-2.5">
|
||||
<div className="absolute left-0 right-0 bottom-0 z-40 px-3 pb-4 pt-2 bg-gradient-to-b from-black/0 via-black/45 to-black/65 flex flex-col gap-2.5">
|
||||
{/* 描述行 */}
|
||||
<div className="flex items-center gap-2.5 mb-1 pointer-events-none">
|
||||
{author.sec_uid ? (
|
||||
<Link
|
||||
href={`/author/${author.sec_uid}`}
|
||||
<Link
|
||||
href={`/author/${author.sec_uid}`}
|
||||
className="flex items-center gap-2.5 pointer-events-auto hover:opacity-80 transition-opacity"
|
||||
>
|
||||
<img src={author.avatar_url!} alt="" className="w-8 h-8 rounded-full" />
|
||||
<span className="text-[15px] leading-tight text-white/95 drop-shadow font-medium">{author.nickname}</span>
|
||||
<img
|
||||
src={author.avatar_url!}
|
||||
alt=""
|
||||
className="w-8 h-8 rounded-full"
|
||||
/>
|
||||
<span className="text-[15px] leading-tight text-white/95 drop-shadow font-medium">
|
||||
{author.nickname}
|
||||
</span>
|
||||
</Link>
|
||||
) : (
|
||||
<div className="flex items-center gap-2.5">
|
||||
<img src={author.avatar_url!} alt="" className="w-8 h-8 rounded-full" />
|
||||
<span className="text-[15px] leading-tight text-white/95 drop-shadow">{author.nickname}</span>
|
||||
<img
|
||||
src={author.avatar_url!}
|
||||
alt=""
|
||||
className="w-8 h-8 rounded-full"
|
||||
/>
|
||||
<span className="text-[15px] leading-tight text-white/95 drop-shadow">
|
||||
{author.nickname}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<span className="text-[13px] leading-tight text-white/95 drop-shadow">·</span>
|
||||
<span className="text-[13px] leading-tight text-white/95 drop-shadow">
|
||||
·
|
||||
</span>
|
||||
<span
|
||||
className="text-[11px] leading-tight text-white/95 drop-shadow"
|
||||
title={formatAbsoluteUTC(createdAt)}
|
||||
@@ -150,16 +196,16 @@ export function MediaControls({
|
||||
|
||||
{/* 播放进度显示 - 所有设备都显示 */}
|
||||
<div className="text-[13px] text-white/90 font-mono min-w-[70px] sm:min-w-[80px]">
|
||||
{isVideo ? (
|
||||
(() => {
|
||||
const v = videoRef?.current;
|
||||
const current = v?.currentTime ?? 0;
|
||||
const total = v?.duration ?? 0;
|
||||
return total > 0 ? `${formatTime(current)} / ${formatTime(total)}` : "--:-- / --:--";
|
||||
})()
|
||||
) : (
|
||||
`${currentIndex + 1} / ${totalSegments}`
|
||||
)}
|
||||
{isVideo
|
||||
? (() => {
|
||||
const v = videoRef?.current;
|
||||
const current = v?.currentTime ?? 0;
|
||||
const total = v?.duration ?? 0;
|
||||
return total > 0
|
||||
? `${formatTime(current)} / ${formatTime(total)}`
|
||||
: "--:-- / --:--";
|
||||
})()
|
||||
: `${currentIndex + 1} / ${totalSegments}`}
|
||||
</div>
|
||||
|
||||
{/* 倍速 - 中等屏幕以上显示,仅视频 */}
|
||||
@@ -231,21 +277,31 @@ export function MediaControls({
|
||||
{/* 循环模式 - 中等屏幕以上显示 */}
|
||||
<button
|
||||
className="hidden md:inline-flex w-[34px] h-[34px] items-center justify-center rounded-full bg-white/15 text-white border border-white/20 backdrop-blur-sm cursor-pointer"
|
||||
onClick={() => onLoopModeChange(loopMode === "loop" ? "sequential" : "loop")}
|
||||
aria-label={loopMode === "loop" ? "循环播放" : "顺序播放"}
|
||||
title={loopMode === "loop" ? "循环播放" : "顺序播放"}
|
||||
onClick={handleLoopModeToggle}
|
||||
aria-label={loopLabel}
|
||||
title={loopLabel}
|
||||
>
|
||||
{loopMode === "loop" ? <Repeat1 size={18} /> : <ArrowDownUp size={18} />}
|
||||
{renderLoopIcon()}
|
||||
</button>
|
||||
|
||||
{/* 适配模式 - 小屏幕以上显示 */}
|
||||
<button
|
||||
className="hidden sm:inline-flex w-[34px] h-[34px] items-center justify-center rounded-full bg-white/15 text-white border border-white/20 backdrop-blur-sm cursor-pointer"
|
||||
onClick={() => onObjectFitChange(objectFit === "contain" ? "cover" : "contain")}
|
||||
aria-label={objectFit === "contain" ? "切换到填充模式" : "切换到适应模式"}
|
||||
title={objectFit === "contain" ? "切换到填充模式" : "切换到适应模式"}
|
||||
onClick={() =>
|
||||
onObjectFitChange(objectFit === "contain" ? "cover" : "contain")
|
||||
}
|
||||
aria-label={
|
||||
objectFit === "contain" ? "切换到填充模式" : "切换到适应模式"
|
||||
}
|
||||
title={
|
||||
objectFit === "contain" ? "切换到填充模式" : "切换到适应模式"
|
||||
}
|
||||
>
|
||||
{objectFit === "contain" ? <Maximize2 size={18} /> : <Minimize size={18} />}
|
||||
{objectFit === "contain" ? (
|
||||
<Maximize2 size={18} />
|
||||
) : (
|
||||
<Minimize size={18} />
|
||||
)}
|
||||
</button>
|
||||
|
||||
{/* 转录文本 - 仅视频且有转录时显示,中等屏幕以上 */}
|
||||
@@ -277,18 +333,28 @@ export function MediaControls({
|
||||
{/* 小屏幕隐藏的适配模式 */}
|
||||
<div className="sm:hidden">
|
||||
<MoreMenuItem
|
||||
icon={objectFit === "contain" ? <Maximize2 size={18} /> : <Minimize size={18} />}
|
||||
icon={
|
||||
objectFit === "contain" ? (
|
||||
<Maximize2 size={18} />
|
||||
) : (
|
||||
<Minimize size={18} />
|
||||
)
|
||||
}
|
||||
label={objectFit === "contain" ? "填充模式" : "适应模式"}
|
||||
onClick={() => onObjectFitChange(objectFit === "contain" ? "cover" : "contain")}
|
||||
onClick={() =>
|
||||
onObjectFitChange(
|
||||
objectFit === "contain" ? "cover" : "contain",
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 中等屏幕以下隐藏的循环模式 */}
|
||||
<div className="md:hidden">
|
||||
<MoreMenuItem
|
||||
icon={loopMode === "loop" ? <Repeat1 size={18} /> : <ArrowDownUp size={18} />}
|
||||
label={loopMode === "loop" ? "循环播放" : "顺序播放"}
|
||||
onClick={() => onLoopModeChange(loopMode === "loop" ? "sequential" : "loop")}
|
||||
icon={renderLoopIcon()}
|
||||
label={loopLabel}
|
||||
onClick={handleLoopModeToggle}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -338,13 +404,13 @@ export function MediaControls({
|
||||
>
|
||||
{isFullscreen ? <Minimize2 size={18} /> : <Maximize size={18} />}
|
||||
</button>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 图文 BGM(隐藏控件,仅用于播放) */}
|
||||
{!isVideo && musicUrl ? <audio ref={audioRef} src={musicUrl} loop preload="metadata" /> : null}
|
||||
{!isVideo && musicUrl ? (
|
||||
<audio ref={audioRef} src={musicUrl} loop preload="metadata" />
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user