Bladeren bron

docs(player): 补充独立播放页一期设计

Codex 4 maanden geleden
bovenliggende
commit
2b3494a63f
1 gewijzigde bestanden met toevoegingen van 214 en 0 verwijderingen
  1. 214 0
      docs/superpowers/specs/2026-04-06-independent-player-page-phase1-design.md

+ 214 - 0
docs/superpowers/specs/2026-04-06-independent-player-page-phase1-design.md

@@ -0,0 +1,214 @@
+# 独立播放页第一阶段设计
+
+## 背景
+
+当前 `entry/src/main/ets/pages/NewIndex.ets` 打开的是独立播放页 `entry/src/main/ets/view/player/MusiPlayerView.ets`,但该页面目前只保留了封面、标题和三个基础按钮,缺少原播放页中的进度条、当前时间、总时长、歌词主体等核心内容。
+
+现象上表现为:
+
+- 播放/暂停图标与实际播放状态可能不同步。
+- 点击上一首/下一首后,封面、歌名、歌手等信息更新不完整。
+- 页面缺少进度条、时间展示、歌词主体,用户感知上和原播放页差距过大。
+
+仓库中原完整播放页仍存在于 `entry/src/main/ets/view/LocalMusic.ets` 的 `IjkMusicPlayerView()` 链路中,但当前需求不是回退到原页面,而是继续保留独立播放页方向,并把原播放页的核心体验逐步迁移到 `MusiPlayerView.ets`。
+
+## 本阶段目标
+
+第一阶段只恢复“核心可用”体验:
+
+- 封面、歌名、歌手在切歌后正确更新。
+- 播放/暂停按钮图标跟随真实播放状态刷新。
+- 进度条、当前时间、总时长恢复展示并持续更新。
+- 歌词主体恢复展示,并随播放进度滚动。
+- 整体页面布局尽量接近原播放页的主骨架。
+
+## 非目标
+
+本阶段不纳入以下功能:
+
+- 更多弹层、播放页更多菜单。
+- 歌词设置面板。
+- 复制歌词、分享歌词。
+- 单行歌词模式。
+- 频谱、长按倍速、复杂手势分支。
+- PIP/蓝牙歌词/投播特化 UI。
+
+这些能力后续如果继续推进独立播放页,可在第二阶段单独迁移。
+
+## 现状与问题归因
+
+`MusiPlayerView.ets` 当前直接通过少量 `@StorageLink` 消费状态:
+
+- `currentSong`
+- `CONTROL_PlayStatus`
+- `progressValue`
+- `cover`
+
+这只能支撑最基础的静态展示,无法完整复用原播放页能力。原播放页中的关键信息还依赖以下链路:
+
+- `playbackPositionMs`、`playbackDurationMs` 驱动当前时间和总时长。
+- `currentSong.lyricContent` 与 `LyricController` 驱动歌词 UI。
+- 播放页内部还维护了当前显示页、歌词滚动位置、封面/歌词切换等展示状态。
+
+因此当前问题不是单个按钮实现问题,而是独立播放页缺少完整的播放态输入和歌词渲染链路。
+
+## 设计概览
+
+保持 `entry/src/main/ets/view/player/MusiPlayerView.ets` 作为独立播放页根组件,不回退到 `LocalMusic.ets` 直接渲染原页面,但补齐独立页自身需要的状态消费和展示层。
+
+页面第一阶段按四层组织:
+
+1. 顶部封面信息层:封面、歌名、歌手。
+2. 中部主体切换层:`Swiper` 两页,第一页展示封面主体,第二页展示歌词主体。
+3. 底部播放控制层:进度条、当前时间、总时长、上一首、播放/暂停、下一首。
+4. 页面内部状态层:当前 `Swiper` 页、点光按钮状态、歌词控制器、派生时间文本。
+
+## 状态输入设计
+
+独立播放页继续以宿主同步到 `AppStorage` 的状态为唯一数据来源,不直接依赖 `LocalMusic.ets` 页面实例字段。
+
+### 直接消费的宿主状态
+
+- `currentSong`
+- `cover`
+- `CONTROL_PlayStatus`
+- `progressValue`
+- `playbackPositionMs`
+- `playbackDurationMs`
+
+### 页面内部派生状态
+
+- `currentTime`
+- `totalTime`
+- 当前 `Swiper` 页索引
+- `LyricController`
+- 点光按钮的 `PointLightOptions`
+
+### 派生规则
+
+- `currentTime` 由 `playbackPositionMs` 转换为 `mm:ss` 或 `hh:mm:ss` 文本。
+- `totalTime` 由 `playbackDurationMs` 转换为时间文本。
+- 当 `currentSong` 或 `currentSong.lyricContent` 变化时,重新解析歌词并更新 `LyricController`。
+- 当 `playbackPositionMs` 变化时,同步刷新 `currentTime`,并推动歌词滚动位置。
+
+## 页面结构设计
+
+### 1. 顶部封面信息层
+
+保留当前独立页的封面背景模糊与主体封面展示,但视觉结构尽量靠拢原播放页:
+
+- 中心展示大封面。
+- 封面下方展示歌名。
+- 歌名下方展示歌手。
+
+封面、歌名、歌手全部直接绑定 `currentSong` 和 `cover`,不保留临时静态文案。
+
+### 2. 中部主体切换层
+
+恢复与原播放页相同的双页结构:
+
+- 第 1 页:封面主体视图。
+- 第 2 页:歌词主体视图。
+
+本阶段只保留最基础的左右切换,不引入更多操作按钮和歌词设置入口。
+
+### 3. 底部播放控制层
+
+底部控制区复用已经独立出的 `entry/src/main/ets/view/player/PlayerControls.ets`,由 `MusiPlayerView.ets` 负责传入:
+
+- `progressValue`
+- `currentTime`
+- `totalTime`
+- `controlPlayStatus`
+- 上一首/播放暂停/下一首回调
+- `onSeek`
+
+这样可以避免在 `MusiPlayerView.ets` 里再次手写一份完整底部控制 UI,也让独立页后续扩展更稳定。
+
+## 歌词设计
+
+本阶段仅恢复“歌词主体显示 + 随进度滚动”。
+
+### 数据来源
+
+优先使用 `currentSong?.lyricContent`。
+
+如果当前歌曲没有内嵌歌词,则歌词区域展示空态提示,不在第一阶段继续接本地 `.lrc` 自动读取、复制歌词、歌词设置等历史能力。
+
+### 控制器
+
+在 `MusiPlayerView.ets` 内创建独立的 `LyricController`,不复用 `LocalMusic.ets` 持有的控制器实例。
+
+### 更新时机
+
+- `currentSong` 切换时:清空旧歌词,解析新歌词并喂给 `LyricController`。
+- `playbackPositionMs` 更新时:调用歌词位置更新逻辑,推动高亮行滚动。
+- 切换到歌词页时:立即同步一次当前位置,避免页面首次切到歌词页时高亮延迟。
+
+## 交互设计
+
+本阶段保留以下基础交互:
+
+- 点击上一首。
+- 点击播放/暂停。
+- 点击下一首。
+- 拖动进度条 seek。
+- 左右切换封面页和歌词页。
+
+本阶段不迁移以下交互:
+
+- 更多菜单。
+- 歌词长按复制。
+- 歌词设置弹层。
+- 长按倍速。
+
+## UI 对齐策略
+
+目标不是逐像素复刻 `LocalMusic.ets`,而是优先恢复用户可感知的主结构一致性。
+
+第一阶段要求:
+
+- 背景继续使用当前歌曲封面模糊。
+- 中部保持“封面/歌词”二页切换结构。
+- 底部恢复时间与进度条。
+- 控制按钮保留当前已验证可用的独立页按钮实现方式。
+
+允许与原页存在的差异:
+
+- 暂不引入更多弹层按钮与歌词设置按钮。
+- 暂不接入频谱和单行歌词。
+- 暂不迁移原页中与特定设备场景绑定的复杂分支。
+
+## 实现步骤
+
+1. 在 `MusiPlayerView.ets` 中补充 `playbackPositionMs`、`playbackDurationMs` 的 `@StorageLink`。
+2. 在页面内部新增时间文本派生逻辑,统一生成 `currentTime` 与 `totalTime`。
+3. 将当前简化中部区域改为 `Swiper`,分别承载封面页与歌词页。
+4. 在 `MusiPlayerView.ets` 中接入 `LyricController`、歌词解析与歌词位置更新逻辑。
+5. 使用 `PlayerControls.ets` 替换当前简化版底部控制区,接入进度与时间。
+6. 复查上一首、播放/暂停、下一首在切歌后是否带动封面、标题、艺术家、歌词与进度一起刷新。
+
+## 验收标准
+
+满足以下条件即可认为第一阶段完成:
+
+- 点击播放/暂停后,按钮图标立即与真实播放状态一致。
+- 点击上一首/下一首后,封面、歌名、歌手同步刷新。
+- 进度条能随播放推进更新。
+- 当前时间与总时长正确显示。
+- 有歌词的歌曲能展示歌词并随进度滚动。
+- 无歌词的歌曲展示空态,页面无异常报错。
+
+## 风险与约束
+
+### 风险 1:歌词链路从原页面剥离时依赖过深
+
+原 `LocalMusic.ets` 中歌词逻辑与设置项、复制歌词、蓝牙歌词、PIP 文本节点等深度耦合。第一阶段必须明确截断边界,只迁移播放页主歌词显示所需的最小链路。
+
+### 风险 2:独立页继续直接堆代码会再次失控
+
+本阶段虽然继续使用 `MusiPlayerView.ets` 作为根组件,但要优先复用已有的独立子组件,例如 `PlayerControls.ets`,避免把原 `LocalMusic.ets` 中的大段 UI 直接整块复制过来。
+
+### 风险 3:状态来源混乱
+
+独立页必须只消费宿主同步后的播放状态,不能一部分走 `AppStorage`,另一部分再去借 `LocalMusic.ets` 实例变量,否则后续仍会出现状态不同步问题。