Files
book-comic-library/docs/superpowers/specs/2026-09-08-cbz-chapter-continuous-reading-design.md

4.7 KiB
Raw Permalink Blame History

CBZ 章节化连续阅读(锁章 + 预读)设计 / CBZ chapter-scoped continuous reading design

日期 2026-09-08。分支 feat/ui-redesign。状态:已获用户批准(会话内确认)。

目标 / Goal

漫画(CBZ)阅读以「章」为单位:一章内无论多少图都视为一段连续的整章内容, 默认不能靠上下滑动越过章界进入下一章(只能点「下一章」),可开「连读」让滑动贯穿章节。 去掉现有 连读/整页/适高 三档概念。锁章时预渲染(挂载+解码)其后 N 章(用户可配置,默认 2), 切章零白屏。 CBZ reading is chapter-scoped: images within a chapter flow continuously; by default scrolling cannot cross a chapter boundary (only the 下一章 button can); an optional 连读 mode lets scrolling flow into following chapters. The three-mode cycle (连读/整页/适高) is removed. The next N chapters (user-configurable, default 2) are pre-rendered while locked so chapter switches are instant.

决策记录 / Decisions

  • 章节内显示 = 按容器宽度(≤720px)连续滚动、无 snap、无适高一屏一页。用户选定(方案 A)。
  • 翻页手势/按键 = 章内翻一屏(scrollBy ±vh),不再按图跳页。用户选定。
  • 采用「按章节作用域渲染」(方案 A),否决:全书渲染+滚动钳制(手感生硬、浪费加载)、 一章一挂载(书签/进度/量高学习跨章迁移复杂、切章闪烁)。
  • 预读实现为隐藏层挂载解码(不增加可滚动高度),不违反锁章语义。用户要求「可预渲染下两章、可配置」。
  • 进度滑条/页码/书签/服务端进度均仍为全书页号,locator 形状 {page} 不变,无后端/API 改动。
  • 扁平包(chapters 为 null)= 整本一章:章按钮与连读开关隐藏,锁章/预读无意义,行为即纯连续滚动。

前端 / Frontend(全部改动在 frontend/src,纯前端)

状态与持久化

  • 删除 PageMode/MODES/MODE_LABEL/useStoredMode 及 cbz-mode 键;PageImg 去掉 fitH 分支; 滚动盒去掉 snap-y snap-proximity。
  • localStorage("cbz-continuous"):连读开关,默认关(锁章)。
  • localStorage("cbz-prefetch"):预读章数 0–3,默认 2。工具栏步进器按钮循环 0→1→2→3→0。

区间几何

  • 当前章 ci 由滚动位置(现 cur 所在页 → 章)推出,章起点 chapters[ci].start。
  • PageHeights 仍覆盖全书 count 页(复用现有量高/估高/applyShift 补偿逻辑)。
  • 挂载上限 mountEnd:锁章 = 第 ci+1+prefetch 章起点(越界取 count);连读 = count。
  • 滚动终点 scrollEnd:锁章 = ph.offset(章末) + 章末卡片高;连读 = 全书末尾。
  • 超出 scrollEnd 的预读页渲染进一个 height:0; overflow:hidden 的绝对定位隐藏层 (子页仍按 ph.offset(i) 定位、aria-hidden):blob 经 fetchObjectUrl 缓存、<img> 挂载即解码, 但不参与滚动几何。切章后这些页落入可见区间即直接上屏。
  • 纯计算抽为 lib/virt.ts 导出 helper(如 chapterRange(chapters, count, ci, continuous, prefetch) → {mountStart, mountEnd, scrollEndPage}),vitest 覆盖。

交互

  • 点按左右侧区/←→/PageUp·Down:scrollBy(±vh, smooth);锁章时滚动高度天然停在章末卡片。
  • 点中间仍唤出工具栏(不变)。
  • 章末卡片:锁章时本章末尾一屏「本章完 · 下一章 →」(最终章显示「全书完」),点卡片=切章。
  • 切章入口(上一章/下一章按钮、目录行、书签 seek、章末卡片、滑条拖入他章区间): 设 ci 并 jump(page);滑条跨章视为显式定位,不算「滑动越界」。
  • 恢复进度:由 initialLocator.page 反推所在章后定位。
  • 闲时「停滚 250ms 预取后 5 页」保留。

错误处理 / Errors

  • 无新增失败面:页数接口、鉴权取图、失败重试(「本页加载失败」)均沿用。
  • cbz-prefetch 值非法(手改 localStorage)→ 回退默认 2;cbz-continuous 非 "1" → 关。

测试 / Tests

  • lib/virt.ts helper 表驱动单测:锁/连读 × 预读 0–3 × 首章/末章/单章扁平包 × 边界页; 断言 mountEnd/scrollEnd 不越过滚动锁、预读不超过 count。
  • 现有 virt 用例保持绿;npm run check 绿。
  • 真实浏览器点验:锁章滚不到下章、开连读可滑过、预读下切章无白屏、扁平包无章 UI。

文档 / Docs

  • docs/CHANGELOG_web.md 新增条目(英中各一行),不重复进 docs/CHANGELOG.md。

不做 / Non-goals

  • 后端章节结构改动、章内页号重映射、PDF/EPUB/TXT 阅读器改动、横翻 RTL 模式、 长按拖拽越章「窥视」下一章。