From 6ec9adc976ba44c63c6b68bd03e71cd60f253fa1 Mon Sep 17 00:00:00 2001 From: truewhile <779943132@qq.com> Date: Fri, 18 Sep 2026 10:09:08 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=A7=BB=E5=8A=A8=E7=AB=AF?= =?UTF-8?q?=E6=92=AD=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/components/PlayerControls.tsx | 75 ++++++-- web/src/components/PlayerDanmakuPanel.tsx | 18 +- .../components/PlayerMobileTheaterInfo.tsx | 166 ++++++++++++++++++ web/src/components/PlayerPlaylistPanel.tsx | 25 ++- web/src/components/playerTheme.ts | 16 ++ web/src/hooks/useIsMobileTheater.ts | 32 ++++ web/src/pages/PlayerPage.tsx | 25 +++ web/src/pages/PlayerVideoStage.tsx | 25 ++- 8 files changed, 351 insertions(+), 31 deletions(-) create mode 100644 web/src/components/PlayerMobileTheaterInfo.tsx create mode 100644 web/src/hooks/useIsMobileTheater.ts diff --git a/web/src/components/PlayerControls.tsx b/web/src/components/PlayerControls.tsx index 3931bc5..c0ac6b0 100644 --- a/web/src/components/PlayerControls.tsx +++ b/web/src/components/PlayerControls.tsx @@ -46,6 +46,9 @@ import { PLAYER_MENU_LABEL, PLAYER_POPOVER, PLAYER_RANGE_OVERLAY, + PLAYER_SHEET, + PLAYER_SHEET_BODY, + PLAYER_SHEET_HEADER, PLAYER_TEXT_BUTTON, PLAYER_TRACK, PLAYER_TRACK_FILL, @@ -193,10 +196,17 @@ type PlayerControlsProps = { streamOffset?: number /** Absolute seek on the full timeline; return true when handled (e.g. HLS restart). */ onSeekAbsolute?: (seconds: number) => boolean + /** + * 竖屏剧场模式:控制栏按钮放大到触控尺寸(44px 起),清晰度/倍速/ + * 选集/字幕弹层改为视频区底部的动作面板,避免右侧抽屉与悬浮小弹层 + * 在窄屏下被裁剪。桌面端保持原样。 + */ + theater?: boolean } export function PlayerControls({ videoRef, + theater = false, volume: volumeProp = 1, onVolumeChange, onVolumeCommit, @@ -910,7 +920,7 @@ export function PlayerControls({ const compactOnlySections = ( <> {showQuality && hasQualityGroups && ( -
+

清晰度

)} -
+

播放速度

{onTogglePlaylist && ( -
+

选集

+
+
{settingsPanel}
+
+
+ ) : null}
{/* 进度条:单独占一行贴着画面底部,轨道只有 3px,悬停才变粗并露出圆点手柄 */}
setSeekHover(null)} > -
+
{tooltipVisible && durationSafe > 0 ? ( @@ -1085,14 +1122,14 @@ export function PlayerControls({ />
-
+
{/* 播放 / 暂停 */} {/* 上一集 / 下一集:单条媒体(电影)没有上下集时整组隐藏,少两个死按钮 */} @@ -1101,7 +1138,7 @@ export function PlayerControls({ {/* 识别到 VR 素材但还没进 VR 时,用一个小圆点提示设置里有东西可开 */} {vr360Detected && !vr360 && ( )} - {settingsMenuOpen && ( + {settingsMenuOpen && !theater && (
{settingsPanel}
@@ -1358,10 +1395,10 @@ export function PlayerControls({
diff --git a/web/src/components/PlayerDanmakuPanel.tsx b/web/src/components/PlayerDanmakuPanel.tsx index 1f9f132..15486db 100644 --- a/web/src/components/PlayerDanmakuPanel.tsx +++ b/web/src/components/PlayerDanmakuPanel.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react' import { Check, ChevronDown, ChevronRight, Eye, EyeOff, Film, Hash, KeyRound, Loader2, MessageSquareText, RefreshCw, Search, Server, Settings2, Sparkles, Tag, X } from 'lucide-react' import type { DanmakuAnime, DanmakuEpisode, DanmakuLoadedInfo } from '../api/danmaku' -import { PLAYER_DRAWER, PLAYER_ICON_BUTTON, PLAYER_PANEL_HEADER } from './playerTheme' +import { PLAYER_DRAWER, PLAYER_ICON_BUTTON, PLAYER_PANEL_HEADER, PLAYER_SHEET, PLAYER_SHEET_BODY, PLAYER_SHEET_HEADER } from './playerTheme' // PlayerDanmakuPanel — the on-player danmaku control panel. It displays // the matched danmaku details (anime title, episode title, comment count, @@ -56,6 +56,11 @@ type PlayerDanmakuPanelProps = { danmakuInfo?: DanmakuLoadedInfo | null onSelectEpisode: (episodeId: number, animeTitle: string, episodeTitle: string) => void onResetAuto: () => void + /** + * 竖屏剧场模式:弹幕面板改为视频区底部的动作面板,而不是右侧抽屉, + * 窄屏下搜索框与滑杆不再被压扁。 + */ + theater?: boolean } export function PlayerDanmakuPanel({ @@ -90,6 +95,7 @@ export function PlayerDanmakuPanel({ danmakuInfo, onSelectEpisode, onResetAuto, + theater = false, }: PlayerDanmakuPanelProps) { const [draft, setDraft] = useState(search) const [advancedOpen, setAdvancedOpen] = useState(false) @@ -201,9 +207,13 @@ export function PlayerDanmakuPanel({ onClick={(e) => e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()} onPointerUp={(e) => e.stopPropagation()} - className={`absolute inset-y-0 right-0 z-30 ${PLAYER_DRAWER}`} + className={ + theater + ? `absolute inset-x-0 bottom-0 z-40 ${PLAYER_SHEET}` + : `absolute inset-y-0 right-0 z-30 ${PLAYER_DRAWER}` + } > -
+
弹幕设置 @@ -214,7 +224,7 @@ export function PlayerDanmakuPanel({
-
+
{/* 弹幕总开关。从操作栏的「弹」按钮直接进来就到了这里,所以这一项就是 用户找的「开关弹幕」;文案跟旧的按钮提示保持一致,避免换个说法让人找不到。 */}